题意:
在一个 4×4 的棋盘上有 8 个黑棋和 8 个白棋,当且仅当两个格子有公共边,这两个格子上的棋是相邻的。移动棋子的规则是交换相邻两个棋子。给出一个初始棋盘和一个最终棋盘,请找出一个最短的移动序列使初始棋盘变为最终棋盘。
输入
前四行,每行 4 个数字(1 或者 0),描述了初始棋盘;接着是一个空行;第六到第九行,每行 4 个数字(1 或者 0),描述了最终棋盘。
输出
输出文件的第一行是一个整数 n,表示最少的移动步数。
思路:
求最短移动路径加上这种棋盘问题很容易想到要用搜索,但直接用的话判断终止条件为遍历两个图是否相等。。很容易超时,然后我们就要想办法把这个判断简化;
发现输入的棋盘是用0和1来表示的,大小也固定为4*4,所以我们可以用16位二进制来表示这个棋盘,为了方便我们将第一个数作为最高位,以此类推。
因为题目要求是相邻(上下左右)两个棋子交换,所以我们可以从四个顶点任意一个向其对角线方向进行搜索,方便起见,我们从左上角向右下搜索。左右交换、上下交换。
最好自己写一下二进制数,方便理解
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <vector>
#include <cmath>
#include <queue>
#include <algorithm>
using namespace std;
struct stu
{
int sum;
int b;
};
int ans1,ans2;
bool vis[70000];
char ch;
int bfs()
{
struct stu a,s;
a.sum=ans1;
a.b=0;
queue<stu> q;
q.push(a);
while(!q.empty())
{
a=q.front();
q.pop();
if(a.sum==ans2)
return a.b;
int temp=a.sum;
for(int i=15;i>=0;i--){
s=a;
int x=(15-i)%4,y=(15-i)/4,z,t=1<<i; //x,y表示当前坐标
//printf("%d %d\n",temp&(1<<i),temp&(1<<i-1));
if(x<3&&(temp&(1<<i))!=(temp&(1<<i-1))) //当前数与左右交换后数不一致则存储
{
z=1<<(i-1);
if(!vis[temp^t^z]) //异或,使两位交换
{
vis[temp^t^z]=true;
s.sum=temp^t^z;
s.b++;
q.push(s);
s.b--;
}
}
//printf("%d %d\n",temp&(1<<i),temp&(1<<i-4));
if(y<3&&(temp&(1<<i))!=(temp&(1<<i-4))) //当前数与上下交换后数不一致则存储
{
z=1<<(i-4);
if(!vis[temp^t^z])
{
vis[temp^t^z]=true;
s.sum=temp^t^z;
s.b++;
q.push(s);
}
}
}
}
}
int main()
{
memset(vis,false,sizeof(vis));
for(int i=15;i>=0;i--){
cin>>ch;
if(ch=='1')
ans1+=1<<i;
}
// cout<<ans1<<endl;
for(int i=15;i>=0;i--){
cin>>ch;
if(ch=='1')
ans2+=1<<i;
}
// cout<<ans2<<endl;
printf("%d\n",bfs());
return 0;
}