ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • DSLR
    백준알고리즘 2019. 9. 1. 00:43

     

    BFS 문제, 사실 처음봤을때 스페셜저지에 이것저것 고려해보니 괴랄한문제라고 생각이많이들었던건데

    마음을 조금진정시키고 차분히 읽어보자.

     

    일단 전체적인 숫자의 길이 수가 4개뿐이며, 그 숫자의 크기가 10000을 넘을수없다.

     

    D일때 2씩 곱해나가면서 만이 넘어갈경우, 나머지를 지정하고

    S는 1씩 감소,

    L은 왼쪽방향으로 숫자이동

    R은 오른쪽방향으로 숫자이동

     

    이 각각의 경우의 수들이 전부 가치가 1씩 이라는점이다.->BFS로 풀수있다.

    하지만, 문제를보면 최소 경로의 지나온 횟수를 물어보는 문제가아니다.

    횟수가 아닌, 목표지점까지 오게된 경로를 구하는것이다,.

    어떻게보면 숨바꼭질4와 비슷하다 생각이됬고 그에 따라 최소경로 구한뒤 -> 트랙킹을했다.

     

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<queue>
    
    using namespace std;
    
    int a[4];
    bool d[10000];
    int c[10000];
    int n,goal;
    void module(int x){
        a[0] = x / 1000;
        a[1] = (x - a[0]*1000)/100;
        a[2] = (x - a[0]*1000 - a[1]*100)/10;
        a[3] = x - a[0]*1000 - a[1]*100-a[2]*10;
    }
    int demodule(int x){
        x = ((a[0]*10+a[1])*10+a[2])*10+a[3];
        return x;
    }
    
    int L(int x){
        module(x);
        int temp = a[0];
        for(int i = 0; i < 3; i++){
            a[i] = a[i+1];
        }
        a[3] = temp;
        return demodule(x);
    }
    int R(int x){
        module(x);
        int temp = a[3];
        for(int i = 3; i >= 1 ; i--){
            a[i] = a[i-1];
        }
        a[0] = temp;
        return demodule(x);
    }
    
    void bfs(){
        queue<int> q;
        q.push(n);
        d[n] = true;
        while(!q.empty()){
            int x = q.front();
            int l = L(x); int r = R(x);
            q.pop();
            if(x == goal){
                return;
            }
            if(d[(x*2) % 10000] == false){
                q.push(((x*2) % 10000));
                d[(x*2)% 10000] = true;
                c[(x*2)%10000] = 1;
            }
            if(x - 1 < 0){
                if(d[9999] == false){
                    q.push(9999);
                    d[9999] = true;
                    c[9999] = 2;
                }
            }
            if(x - 1 >= 0){
                if(d[x-1] == false){
                    q.push(x-1);
                    d[x-1] = true;
                    c[x-1] = 2;
                }
            }
            if(d[l]== false){
                q.push(l);
                d[l] = true;
                c[l] = 3;
            }
            if(d[r] == false){
                q.push(r);
                d[r] = true;
                c[r] = 4;
            }
        }
    }
    vector<char> visit;
    void tracking(){
        int x = goal;
        visit.clear();
        while(true){
            if(x == n){
                break;
            }
            if(c[x] == 1){
                if(d[(x + 10000)/2] == true){
                    x = (x + 10000)/2;
                    visit.push_back('D');
                }
                else if(d[x/2] == true){
                        x = x/2;
                        visit.push_back('D');
                }
            }
            if(c[x] == 2){
                if(x == 0){
                    x = 9999;
                    visit.push_back('S');
                }
                else{
                    x -=1;
                    visit.push_back('S');
                }
            }
            if(c[x] == 3){
                x = R(x);
                visit.push_back('L');
            }
            if(c[x] == 4){
                x = L(x);
                visit.push_back('R');
            }
    
        }
    }
    
    int main(){
        int tc;
        cin>>tc;
        for(int t =0; t<tc; t++) {
            cin >> n >> goal;
            bfs();
    
            tracking();
            for(int i = 0; i < visit.size(); i++){
                cout<<visit[i];
            }
            cout<<'\n';
            memset(d,false,sizeof(d));
            memset(c,0,sizeof(c));
        }
    }

     

    위와 같이 트랙킹을해서 char 벡터에, 지나온 경로를 담고 나중에 출력하는방식이였지만, 전체적으로 BFS하는횟수도 많거니와

    그에따라 트랙킹의 갯수도 늘어나면 늘어날수록 시간복잡도가 기하급수적으로 증가하기때문에, 시간초과가 나버렸다.

     

    이때 내가할수있는 방식변경법은, 경로를 구하고 -> 다시 돌아가면서 체킹한다 가 아닌,

    " 경로를 구하면서, 갔던방법을 체킹한다 "이다.

     

    경로를 가면서 체킹하는 방법으로 생각난게,

    어차피 출력이 문자열 이기떄문에, string 배열을 만들어서 최초경로에서부터 하나하나 담아가는것을생각했다.

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<queue>
    
    using namespace std;
    
    int a[4];
    bool d[10000];
    string ans[10000];
    
    
    int L(int x){
        a[0] = x / 1000;
        a[1] = (x - a[0]*1000)/100;
        a[2] = (x - a[0]*1000 - a[1]*100)/10;
        a[3] = x - a[0]*1000 - a[1]*100-a[2]*10;
        int temp = a[0];
        for(int i = 0; i < 3; i++){
            a[i] = a[i+1];
        }
        a[3] = temp;
        x = a[0]*1000 + a[1]*100 + a[2] *10 +a[3];
        return x;
    }
    int R(int x){
        a[0] = x / 1000;
        a[1] = (x - a[0]*1000)/100;
        a[2] = (x - a[0]*1000 - a[1]*100)/10;
        a[3] = x - a[0]*1000 - a[1]*100-a[2]*10;
        int temp = a[3];
        for(int i = 3; i >= 1 ; i--){
            a[i] = a[i-1];
        }
        a[0] = temp;
        x = a[0]*1000 + a[1]*100 + a[2] *10 +a[3];
        return x;
    }
    
    void bfs(int n, int goal){
        queue<int> q;
        q.push(n);
        d[n] = true;
        ans[n] = "";
        while(!q.empty()){
            int x = q.front();
            int l = L(x); int r = R(x);
            q.pop();
            if(x == goal){
                return;
            }
            if(d[(x*2) % 10000] == false){
                q.push(((x*2) % 10000));
                d[(x*2)% 10000] = true;
                ans[(x*2)%10000] = ans[x] + "D";
            }
    
            if(x - 1 >= 0){
                if(d[x-1] == false){
                    q.push(x-1);
                    d[x-1] = true;
                    ans[x-1] = ans[x] + "S";
                }
            }
            if(x -1 < 0) {
                if(d[9999] == false){
                    q.push(9999);
                    d[9999] = true;
                    ans[9999] = ans[x]+ "S";
                }
            }
            if(d[l]== false){
                q.push(l);
                d[l] = true;
                ans[l] = ans[x] +"L";
            }
            if(d[r] == false){
                q.push(r);
                d[r] = true;
                ans[r] =ans[x]+"R";
            }
        }
    }
    
    int main(){
        int tc;
        cin>>tc;
        for(int t =0; t<tc; t++) {
            int n,goal;
            cin >> n >> goal;
            bfs(n,goal);
    
            cout<<ans[goal];
            cout<<'\n';
            memset(d,false,sizeof(d));
        }
    }

     

    이런식으로, 이전경로에서 하나하나씩 문자열을담아가면, BFS자체에 시간복잡도내에서 충분히 최종목표치까지의

    시간복잡도로 합쳐져서 괜찮은방법이라고 생각했다.

    그리고 memset 을 방문경로에 대해서만 썼는데, 어차피 BFS내 조건에서 최초 시작점만 초기화해주면, 다른곳에 들어갈때 최초경로 + 지금경로로 입력되기떄문에, 시간복잡도를 한층 더줄이는 방법이라고 생각했다.

     

    -구현하기 까다로웠던 문제-

    '백준알고리즘' 카테고리의 다른 글

    레이저 통신  (0) 2019.09.01
    물통  (0) 2019.09.01
    숨바꼭질4  (0) 2019.08.31
    합이 0인 네 정수  (0) 2019.08.31
    말이 되고픈 원숭이  (0) 2019.08.31

    댓글

Designed by Tistory.