https://www.acmicpc.net/problem/2529

 

2529번: 부등호

두 종류의 부등호 기호 ‘<’와 ‘>’가 k개 나열된 순서열  A가 있다. 우리는 이 부등호 기호 앞뒤에 서로 다른 한 자릿수 숫자를 넣어서 모든 부등호 관계를 만족시키려고 한다. 예를 들어, 제

www.acmicpc.net

 

문제의 조건 

1. k 는 2보다 크거나 같고, 9보다 작거나 같다

2. k+1개의 숫자는 서로 다른 숫자이다

3. k개의 부등호에 맞는 값 중 최대값, 최소값을 구해야 한다

 

 

부등호에 '<=', '>=' 이 존재하고 서로 다른 숫자라는 조건이 없었다면, 경우의 수가 1e10 이 돼 TLE이 나겠지만, 

서로 다른 k+1 개의 수를 찾기 때문에 (k+1)! 의 경우의 수만 찾으면 된다.

 

처음에는 재귀함수를 통해 백트랙킹으로 풀었다.

 

소스코드 (백트랙킹)

숫자가 최대 10개이므로 비트마스크로 사용한 숫자를 체크하며, 조건이 맞지 않으면 재귀를 탈출하고, 

k+1 개의 숫자를 다 채우면 1을 리턴하면서 종료된다.

 

최솟값은 0부터 시작해서 찾아야하고, 최댓값은 9부터 시작해서 내려가며 찾아야하기 때문에

같은 재귀라도 반복문을 달리 사용해야하는 번거로움이 있었다.

 

  1. #include<stdio.h>
  2. #include<vector>
  3. #include<algorithm>
  4. #include<string>
  5. using namespace std;
  6. typedef long long ll;
  7. int k;
  8. char karr[11];
  9. string min_="", max_="";
  10. bool inline chk(int a, char c, int b){
  11. return c == '>' ? a>b : a<b;
  12. }
  13. string num(vector<int>& ans){
  14. string s = "";
  15. for(int i=0; i < ans.size(); i++){
  16. s += ans[i] + '0';
  17. }
  18. return s;
  19. }
  20. int solve(vector<int>& ans, int visited, int p, int ismax){
  21. if(p == k) {
  22. return 1;
  23. }
  24. int cur = ans[p];
  25. int init = ismax ? 9 : 0;
  26. int iter = ismax ? -1 : 1;
  27. int end = ismax ? -1 : 10;
  28. for(int pp1=init; pp1 != end; pp1+=iter){
  29. if(visited & (1<<pp1))
  30. continue;
  31.  
  32. if(chk(cur, karr[p], pp1)){
  33. ans.push_back(pp1);
  34. if(solve(ans, visited | (1<<pp1), p+1, ismax))
  35. return 1;
  36. ans.pop_back();
  37. }
  38. }
  39. return 0;
  40. }
  41. string solve_(int i, int ismax){
  42. int visited = 0;
  43. vector<int>ans;
  44.  
  45. ans.push_back(i);
  46. visited |= (1<<i);
  47. if(solve(ans, visited, 0, ismax)){
  48. return num(ans);
  49. }
  50. return "";
  51. }
  52. int main(){
  53. scanf("%d", &k);
  54. for(int i=0;i<k;i++){
  55. scanf(" %c", &karr[i]);
  56. }
  57.  
  58. for(int i=0; i<10; i++){
  59. min_ = solve_(i, 0);
  60. if(min_ != "") break;
  61. }
  62.  
  63. for(int i=9; i>=0; i--){
  64. max_ = solve_(i, 1);
  65. if(max_ != "") break;
  66. }
  67. printf("%s\n%s\n", max_.c_str(), min_.c_str());
  68. return 0;
  69. }

 

 

풀다보니 최소값은 0~k 의 숫자, 최대값은 9~9-k 까지의 숫자만 사용하면 부등호를 만족하는 순서를 찾을 수 있기 때문에,

k 가 9인 경우 최대 10! 까지만 돌기 때문에 조합으로 풀 수 있겠다 생각됐다.

 

 

소스코드 (조합)

조합으로 푸니 코드가 깔끔해졌다.. 진작 이렇게 풀걸

처음부터 조합으로 풀 생각을 할 수 있도록 문제 해결 전략을 잘 세워야겠다..

 

  1. #include<stdio.h>
  2. #include<algorithm>
  3. #include<vector>
  4. using namespace std;
  5. int k;
  6. char karr[10];
  7. bool inline chk_(int a, char c, int b){
  8. return c == '>' ? a>b : a<b;
  9. }
  10. int chk(vector<int>& cand){
  11. for(int i=0; i<k; i++){
  12. if(!chk_(cand[i], karr[i], cand[i+1]))
  13. return 0;
  14. }
  15. return 1;
  16. }
  17. int main(){
  18. scanf("%d", &k);
  19. for(int i=0; i<k;i++)
  20. scanf(" %c", &karr[i]);
  21.  
  22. vector<int> min_ = vector<int>(k+1), max_ = vector<int>(k+1);
  23. for(int i=0; i<=k; i++){
  24. min_[i] = i;
  25. max_[i] = 9-i;
  26. }
  27.  
  28. do{
  29. if(chk(min_)) break;
  30. }while(next_permutation(min_.begin(), min_.end()));
  31.  
  32. do{
  33. if(chk(max_)) break;
  34. }while(prev_permutation(max_.begin(), max_.end()));
  35.  
  36. for(int i=0; i<=k; i++)
  37. printf("%d",max_[i]);
  38. printf("\n");
  39. for(int i=0; i<=k; i++)
  40. printf("%d",min_[i]);
  41. printf("\n");
  42. return 0;
  43. }

 

 

 

 

 

'알고리즘 문제풀이' 카테고리의 다른 글

BOJ 14503 로봇 청소기  (0) 2021.06.12
BOJ 1969 DNA  (0) 2021.05.24
BOJ 17472 다리만들기 2  (0) 2020.11.24
BOJ 1305 광고  (0) 2020.09.24
BOJ 10826 피보나치 수 4  (0) 2020.09.04

 풀이 방법

 

이 문제는 두 지점 사이의 최소 거울 수를 구하는 문제로, 평소 두 지점의 최단거리를 구할 때 사용하는 BFS 를 응용해 "거리" 대신 "거울 수(방향 전환이 일어나는 수)" 를 사용하여 풀 수 있다고 생각했다.

 

단순 좌표 (y, x) 뿐만 아니라, 사용된 거울 수 및 이동해온 방향 정보가 필요하므로 새로운 구조체를 정의한다.

struct cell{
    int y, x, num, p_dir;
    cell(int y,int x,int num, int p_dir):y(y), x(x), num(num), p_dir(p_dir) {};
};

이동해온 방향(p_dir)을 저장하는 이유는 거울이 사용되는 지점이 방향이 전환되는 시점이기 때문에, 현재 좌표가 어느 방향에서 왔는지 확인하기 위해 필요하다.

 

이 구조체를 이용해 사용된 거울 수(num) 이 가장 작은 순으로 queue 에서 가져오기 위해 우선순위 큐를 사용한다.

 

 

 

 

 우선순위 큐의 Comparator 정의

내가 정의한 구조체에서 max-heap 으로 사용할지  min-heap 으로 사용하지 결정하기 위해 compartor를 정의해야 한다.

방법은 구사과 님의 블로그에 자세히 설명돼있다.

 

풀이에 사용된 방법은 구조체를 이용해 operator() 를 정의해 사용했다.

struct cmp{
    bool operator()(const struct cell& a, const struct cell& b){
        return a.num > b.num;
    }
};

// 중략

priority_queue< cell, vector<cell>, cmp > pq;

 

* 참고로 greater 는 작은 값을 먼저, less 는 큰 값을 먼저 리턴한다.

* a.num > b.num 는 greater 와 같고, a.num < b.num 은 less 와 같다.

 

이렇게 정의된 우선순위 큐를 이용하면, 시작지점에서 거울을 사용하지 않고 최대한 이동하여 탐색한 후,

거울  1개, 2개, k개 사용해서 이동할 수 있는 위치를 순차적으로 탐색할 수 있다.

 

 

 

 

 소스코드

  1. #include<stdio.h>
  2. #include<vector>
  3. #include<queue>
  4.  
  5. using namespace std;
  6.  
  7. struct cell{
  8. int y, x, num, p_dir;
  9. cell(int y,int x,int num, int p_dir):y(y), x(x), num(num), p_dir(p_dir) {};
  10. };
  11. struct cmp{
  12. bool operator()(const struct cell& a, const struct cell& b){
  13. return a.num > b.num;
  14. }
  15. };
  16.  
  17. /*
  18. bool operator<(const struct cell a, const strcut cell b){
  19.   return a.num > b.num;
  20. }
  21. */
  22. int w,h, sy=-1,sx=-1, dy,dx;
  23. int dix[4] = {-1,1,0,0}, diy[4] = {0,0, -1, 1};
  24. char board[111][111];
  25. bool visited[111][111];
  26. bool dir_check(int a, int b){
  27. return (a<2 && b<2) || (a>=2 && b>=2) || a == -1;
  28. }
  29. int main(){
  30. scanf("%d%d",&w,&h);
  31.  
  32. for(int i=0;i<h;i++){
  33. scanf("%s", board[i]);
  34. for(int j=0;j<w;j++){
  35. if(board[i][j] == 'C'){
  36. if(sy != -1 && sx != -1){
  37. dy = i, dx = j;
  38. } else {
  39. sy = i, sx = j;
  40. }
  41. }
  42. }
  43. }
  44. priority_queue< cell, vector<cell>, cmp > pq;
  45. // priority_queue< cell, vector<cell>, greater<cell> > pq;
  46.  
  47. pq.push(cell(sy, sx, 0, -1));
  48. while(!pq.empty()){
  49. cell cur = pq.top();
  50. pq.pop();
  51. visited[cur.y][cur.x] = 1;
  52. if(cur.y == dy && cur.x == dx){
  53. printf("%d\n", cur.num);
  54. break;
  55. }
  56. for(int i=0;i<4;i++){
  57. int ny = cur.y + diy[i], nx = cur.x + dix[i];
  58. if(ny >=0 && nx >=0 && nx < w && ny < h) {
  59. if(!visited[ny][nx] && board[ny][nx] != '*'){
  60. // printf("\tnext : (%d, %d) num : %d, is_plus? : %d\n", ny,nx, cur.num, !dir_check(cur.p_dir, i));
  61. pq.push(cell(ny, nx, cur.num + (dir_check(cur.p_dir, i) ? 0 : 1), i));
  62. }
  63. }
  64. }
  65. }
  66. return 0;
  67. }

 

 

 

'알고리즘 문제풀이' 카테고리의 다른 글

BOJ 2580 스도쿠  (0) 2020.08.20
BOJ 1194 달이 차오른다, 가자.  (0) 2020.08.11
BOJ 11505 구간 곱 구하기  (0) 2020.08.06
1344 축구  (0) 2018.06.01
1351 무한 수열  (0) 2018.05.28

+ Recent posts