한창 완전탐색 알고리즘 문제를 풀다가 이 문제를 마주쳤는데, 계속 메모리초과가 떴다.

완전탐색은 DFS(재귀)로 풀어야 한다는 안일한 생각때문에 3일을 쓴 것 같다...ㅋㅋㅋㅋㅋ

시간 제한 메모리 제한 정답률
2초 256MB 55.583%

 

문제

소수를 유난히도 좋아하는 창영이는 게임 아이디 비밀번호를 4자리 ‘소수’로 정해놓았다. 어느 날 창영이는 친한 친구와 대화를 나누었는데:

  • “이제 슬슬 비번 바꿀 때도 됐잖아”
  • “응 지금은 1033으로 해놨는데... 다음 소수를 무엇으로 할지 고민중이야"
  • “그럼 8179로 해”
  • “흠... 생각 좀 해볼게. 이 게임은 좀 이상해서 비밀번호를 한 번에 한 자리 밖에 못 바꾼단 말이야. 예를 들어 내가 첫 자리만 바꾸면 8033이 되니까 소수가 아니잖아. 여러 단계를 거쳐야 만들 수 있을 것 같은데... 예를 들면... 1033 1733 3733 3739 3779 8779 8179처럼 말이야.”
  • “흠...역시 소수에 미쳤군. 그럼 아예 프로그램을 짜지 그래. 네 자리 소수 두 개를 입력받아서 바꾸는데 몇 단계나 필요한지 계산하게 말야.”
  • “귀찮아”

그렇다. 그래서 여러분이 이 문제를 풀게 되었다. 입력은 항상 네 자리 소수만(1000 이상) 주어진다고 가정하자. 주어진 두 소수 A에서 B로 바꾸는 과정에서도 항상 네 자리 소수임을 유지해야 하고, ‘네 자리 수’라 하였기 때문에 0039 와 같은 1000 미만의 비밀번호는 허용되지 않는다.

입력

첫 줄에 test case의 수 T가 주어진다. 다음 T줄에 걸쳐 각 줄에 1쌍씩 네 자리 소수가 주어진다.

출력

각 test case에 대해 두 소수 사이의 변환에 필요한 최소 회수를 출력한다. 불가능한 경우 Impossible을 출력한다.

 

핵심

  • 최단경로를 구하는 문제라고 생각하면 쉽다!
  • 당근 BFS를 이용하면 어렵지 않게 풀 수 있다.

코드

#include <iostream>
#include <algorithm>
#include <string>
#include <queue>


using namespace std;

int t;
string s1, s2;
bool is_prime[10010];
int visited[10010];

//먼저 소수를 미리 구해놓는다.
void make_prime(){
    is_prime[0] = true;
    is_prime[1] = true;
    
    for(int i=2;i*i<=10000;i++){
        if(is_prime[i]) continue;
        
        for(int j=i*i;j<=10000;j+=i){
            is_prime[j] = true;
        }
    }
    
    for(int i=0;i<=10000;i++) is_prime[i] = !is_prime[i];
}

//너비우선탐색으로 최단거리를 구한다. 이때 1000미만은 빼야하므로 주의하자.(문제 조건)
int bfs(){
    queue<string> q;
    string cur, next;
    
    q.push(s1);
    visited[stoi(s1)] = 1;
    
    while(!q.empty()){
        
        cur = q.front();
        q.pop();
        
        for(int i=0;i<4;i++){
            for(char c='0';c<='9';c++){
                if(c=='0' && i==0) continue;
                
                next = cur;
                next[i] = c;
                int snext = stoi(next);
                
                if(is_prime[snext] && visited[snext] == 0){
                    q.push(next);
                    visited[snext] = visited[stoi(cur)] + 1;
                }
            }
        }
        
        
    }
    
    //첫 시작을 1로 했으므로 마지막에 1 빼줘야 정답임.
    return visited[stoi(s2)] - 1;
}



int main(int argc, char* argv[]) {
	ios_base :: sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
    
    make_prime();
    
    
    cin >> t;
    
    while(t--){
        cin >> s1 >> s2;
        
        int b = bfs();

        if(b == -1){
            cout << "Impossible\n";
        }
        else{
            cout << b << '\n';
        }
        
        //visited 배열은 재사용
        for(int i=0;i<=10000;i++){
            visited[i] = 0;
        }
    }
	
	return 0;
}

해설

너비우선탐색문제였고, BFS로 접근했다면 어렵지 않은 문제이다.

문제 조건만 주의하면 된다.

나는 완전탐색이면 다 DFS문제인줄 알고 풀어서 계속 메모리초과가 났다.

한창 백트래킹 문제풀다가 이거 푸니까 당연히 DFS겠지라고 생각해버렸다ㅜ

 

우선, 1~10000까지 소수를 모두 구한다.(에라토스테네스의 체 원리 이용)

그 후, 처음 시작점에서 BFS를 돌리면 된다.

BFS돌릴때 네 자리 수를 하나하나씩 다 바꿔보고, 소수인지와 이미 방문했는지를 검사한다.

검사를 완료한 노드를 큐에 넣고 계속 BFS를 돌려주면 된다.

하고 나서 visited[]배열만 초기화 잘 해주면 어려울 것이 없다.

 

나 같은 경우, 이번 문제에서 입력값을 string으로 받았다.

string 헤더파일의 stoi()함수를 이용하면 int로 변환된 값을 쉽게 얻을 수 있어서 유용하게 사용했다.

  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기