题目
【问题描述】
在一个 4x4 的棋盘上有 8 个黑棋和 8 个白棋,当且仅当两个格子有公共边时,这两个格
子上的棋是相邻的。移动棋子的规则是交换相邻的两个棋子。现在给出一个初始棋盘和一个
最终棋盘,要求你找出一个最短的移动序列使初始棋盘变为最终棋盘。
【输入】
第一到第四行每行 4 个数字(1 或者 0),描述初始棋盘。
接着是一个空行。
第六道第九行每行 4 个数字,描述最终棋盘。
【输出】
输出文件的第一行是一个整数 n,表示最少的移动步数。
思路
状态压缩矩阵存储成数字,能减少空间分配,哈希转化,BFS扩展判断下边和右边即可,因为这样所有点都可以扩展到。
代码实现
#include<bits/stdc++.h>
using namespace std;
const int maxn=65536+10;
char m[10][10];
int s,e;
queue<int>q;
int op[maxn];
bool vis[maxn];
//矩阵变数字
inline int h(char a[][10]){
int ans=0;
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
ans<<=1;
ans|=(a[i][j]-'0');
}
}
return ans;
}
int main(){
for(int i=0;i<4;i++)scanf("%s",m[i]);
s=h(m);
//printf("s %d\n",s);
q.push(s);
vis[s]=true;
getchar();
for(int i=0;i<4;i++)scanf("%s",m[i]);
e=h(m);
//printf("e %d\n",e);
//BFS
while(!q.empty()){
int t=q.front();
//printf("%d\n",t);
q.pop();
if(t==e){
printf("%d\n",op[t]);
return 0;
}
int t2=t;//防止变化带来的更改
//反向数字变矩阵
for(int i=3;i>=0;i--){
for(int j=3;j>=0;j--) {
if((t2&1)==0)m[i][j]='0';
else m[i][j]='1';
t2>>=1;
}
}
//矩阵扩展
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
//右
if(j<3&&m[i][j]!=m[i][j+1]){
swap(m[i][j],m[i][j+1]);
int l=h(m);
swap(m[i][j],m[i][j+1]);
if(!vis[l]){
vis[l]=true;
q.push(l);
op[l]=op[t]+1;
}
}
//下
if(i<3&&m[i][j]!=m[i+1][j]){
swap(m[i][j],m[i+1][j]);
int l=h(m);
swap(m[i][j],m[i+1][j]);
if(!vis[l]){
vis[l]=true;
q.push(l);
op[l]=op[t]+1;
}
}
}
}
}
return 0;
}