BFS 문제중에 좀 특이하게 맵 탐색이 아닌 다른 공간을 탐색하는 문제이다. 어떤 4자리 소수에서 각 자리수를 바꿔가며 다른 소수로 이동하면서 탐색을 하게 된다.
따라서 일단 소수를 찾아놓고, 각 자리수를 바꾼 숫자가 소수인지 확인해야 한다.
소수를 찾아놓을 땐, 에라토스테네스의 체를 이용해 정해진 범위(10000)까지의 소수를 찾는다.
memset(filter, 1, sizeof(filter));
filter[0] = 0;
filter[1] = 0;
for (int i = 2; i < 10000; i++)
{
if (filter[i])
{
for (int j = 2*i; j < 10000; j+=i)
filter[j] = 0;
}
}
그리고, 탐색을 하는 과정에서 각 자리수를 바꿔야 하므로 조금 더 편하게 이 과정을 하기 위해 현재 상태 숫자를 나타내는 구조체를 만들어 둔다. 구조체에는 숫자가 4개의 배열로 각 자리수를 가진다. 시작점에서 현재까지 몇번 바뀌었는지를 세는 변수 count와, 연산을 편하게 하기 위해 숫자를 리턴하는 함수, 각 자리수에 대한 접근을 하기 위해 operator[]도 작성했다.
struct number{
int num[4];
int count;
int getNumber(){
return this->num[0] * 1000 + this->num[1] * 100 + this->num[2] * 10 + this->num[3];
}
int& operator[](int idx){
return num[idx];
}
number(int input, int count){
this->num[3] = input % 10; input /= 10;
this->num[2] = input % 10; input /= 10;
this->num[1] = input % 10; input /= 10;
this->num[0] = input % 10; input /= 10;
this->count = count;
}
number() {}
};
BFS탐색은 다른것과 유사하게 큐에서 뽑고, 종료조건인지 확인한 후 탐색을 하면서 조건에 맞는것만 큐에 넣는 방식으로 진행된다. 이 때, 먼저 만들어둔 에라토스테네스의 체를 사용해 각 숫자가 소수인지 확인할 수 있다. visit[] 배열도 똑같이 그 공간만큼 만들고, 탐색을 진행하면서 표시하게 된다.
int S, E;
std::cin >> S >> E;
std::queue<number> Q;
bool visit[10001];
bool found = false;
memset(visit, 0, sizeof(visit));
Q.push(number(S, 0));
while (!Q.empty())
{
number cur = Q.front();
Q.pop();
if (cur.getNumber() == E){
std::cout << cur.count << '\n';
found = true;
break;
}
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 10; j++)
{
number next(cur.getNumber(), cur.count + 1);
next[i] = j;
if (next.getNumber() > 999 && next.getNumber() < 10000 && filter[next.getNumber()] && !visit[next.getNumber()])
{
Q.push(next);
visit[next.getNumber()] = 1;
}
}
}
}
if (!found)
std::cout << "Impossible" << '\n';
탐색은 2D 맵과 거의 똑같다. 2차원으로 진행되는 탐색이 그냥 1차원으로 바뀌기만 한 것 같다.
전체 코드는 다음과 같다.
#include <iostream>
#include <queue>
#include <string.h>
// PASS
int filter[10001];
struct number{
int num[4];
int count;
int getNumber(){
return this->num[0] * 1000 + this->num[1] * 100 + this->num[2] * 10 + this->num[3];
}
int& operator[](int idx){
return num[idx];
}
number(int input, int count){
this->num[3] = input % 10; input /= 10;
this->num[2] = input % 10; input /= 10;
this->num[1] = input % 10; input /= 10;
this->num[0] = input % 10; input /= 10;
this->count = count;
}
number() {}
};
int main()
{
memset(filter, 1, sizeof(filter));
filter[0] = 0;
filter[1] = 0;
for (int i = 2; i < 10000; i++)
{
if (filter[i])
{
for (int j = 2*i; j < 10000; j+=i)
filter[j] = 0;
}
}
// for (int i = 0; i < 10000; i++)
// if(filter[i])
// std::cout << i << " ";
int T;
std::cin >> T;
for (int run = 0; run < T; run++)
{
int S, E;
std::cin >> S >> E;
std::queue<number> Q;
bool visit[10001];
bool found = false;
memset(visit, 0, sizeof(visit));
Q.push(number(S, 0));
while (!Q.empty())
{
number cur = Q.front();
Q.pop();
if (cur.getNumber() == E){
std::cout << cur.count << '\n';
found = true;
break;
}
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 10; j++)
{
number next(cur.getNumber(), cur.count + 1);
next[i] = j;
if (next.getNumber() > 999 && next.getNumber() < 10000 && filter[next.getNumber()] && !visit[next.getNumber()])
{
Q.push(next);
visit[next.getNumber()] = 1;
}
}
}
}
if (!found)
std::cout << "Impossible" << '\n';
}
}
BFS, DFS는 계속 꾸준히 봐야겠다. 이게 어느정도 꼬는게 LCA같은 경우 원래 로직 틀에서 많이 바뀌지는 않았는데, BFS DFS의 경우 특이하게 꼬는 문제나 Cost계산, Visit처리의 다양성을 보면 진짜 엄청나게 많은 유형으로 만들 수 있는 것 같다.
오늘도 평온한 하루가 되길. 슨민.
'짜잘한 기록' 카테고리의 다른 글
백준 1167 트리의 지름 (0) | 2021.09.09 |
---|---|
백준 10026 적록색약 (0) | 2021.09.09 |
백준 2206 벽 부수고 이동하기 (0) | 2021.09.07 |
[나만 몰랐던 알고리즘] 2D 맵 BFS (0) | 2021.09.06 |
백준 1626 두 번째로 작은 스패닝 트리 (0) | 2021.09.06 |