맵 사이즈가 5×5가 최대이기 때문에 (그리고 실제로 이동 가능한 곳은 4×4이기 때문에) 단순 BFS로 풀린다. 어렵게 접근한 것 자체가 잘못. 계속 시도하다가 WA가 난 정렬 후 비교 방식은 실제로 다른 row를 같은 row로 판정할 수 있기 때문에 안된다.
몇 가지 Util 함수들을 만들면, 판 자체를 BFS 과정에서 들고 다닐 수 있고, 쉽게 비교할 수 있다. 또한 visited 체크는 판 전체를 stringify 한 후, set을 이용해서 체크하는 방식을 사용했다.
👉 펼치기
int H, C;
structBoard{
int b[5][5];
int(&operator[](int idx))[5]{
return b[idx];
}
} A, B;
boolcmp(Board& a, Board& b){
for(int i =0; i < H;++i){
for(int j =0; j < C;++j){
if(a[i][j]== b[i][j])continue;
returnfalse;
}
}
returntrue;
}
unordered_set<string> US;
voidvisitedReset(){
US.clear();
}
string getHash(Board& a){
string str ="";
for(int i =0; i < H;++i){
for(int j =0; j < C;++j){
str +=to_string(a[i][j])+" ";
}
}
return str;
}
boolcheckVisited(Board& a){
string hash =getHash(a);
if(US.find(hash)== US.end())returnfalse;
returntrue;
}
voidregisterVisited(Board& a){
string hash =getHash(a);
US.insert(hash);
}
structQData{
int depth;
Board b;
} q[1000000];
int qf, qr;
voidsolve(){
scanf("%d %d",&H,&C);
FOR(i,0,H)FOR(j,0,C)scanf("%d",&A[i][j]);
FOR(i,0,H)FOR(j,0,C)scanf("%d",&B[i][j]);
int ans =0x7FFFFFFF;
Board a = A;
visitedReset();
// bfs
qf = qr =0;
q[qr++]={0, a };
while(qf != qr){
auto& cur = q[qf++];
if(checkVisited(cur.b))continue;
registerVisited(cur.b);
if(cmp(cur.b, B)){
ans = cur.depth;
break;
}
for(int p =0; p < H -1;++p){
Board b = cur.b;
for(int i =0; i < C;++i)swap(b[p][i], b[p+1][i]);
q[qr++]={cur.depth +1, b};
}
for(int p =0; p < C -1;++p){
Board b = cur.b;
for(int i =0; i < H;++i)swap(b[i][p], b[i][p+1]);
q[qr++]={cur.depth +1, b};
}
}
if(ans !=0x7FFFFFFF){
printf("%d\n", ans);
}else{
printf("-1\n");
}
}
정해는 next_permutation을 활용한다. 내가 안써본 STL인데, 지금보니 상당히 유용하다. 직접 구현하려면, 뒤쪽부터 '내림차순'이 끝나는 지점을 파악해서, 그 내림차순을 뒤집은 뒤, 내림차순 뒤집고 나서 맨 앞의 숫자를 swap 하면 된다. 규칙은 쉽게 발견할 수 있을 것. next_permutation을 구하는 데에는 O(N)이 걸린다. 또한, 단순히 오름차순과 내림차순이 중요하기 때문에, 조합으로도 쉽게 활용할 수 있다. ([0,1,1] 와 같은 배열을 만들어서 쓰면 된다. 내림차순으로만 바꿔줌에 유의. 또는 [1,1,0] 으로 한 뒤 prev_permutation을 사용해도 된다. 그동안은 bitset만 썼는데 이 방식도 교육적이다.)