먼지의삶 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내 조건에서 최초 시작점만 초기화해주면, 다른곳에 들어갈때 최초경로 + 지금경로로 입력되기떄문에, 시간복잡도를 한층 더줄이는 방법이라고 생각했다.

 

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