棋盘游戏
https://loj.ac/problem/10029
题目描述
在一个\(4×4\)的棋盘上有\(8\)个黑棋和\(8\)个白棋,当且仅当两个格子有公共边,这两个格子上的棋是相邻的。移动棋子的规则是交换相邻两个棋子。给出一个初始棋盘和一个最终棋盘,请找出一个最短的移动序列使初始棋盘变为最终棋盘,输出最短序列的长度。
思路
数据如此之小,显然是要我们搜索。而这里对棋盘可以直接简单的压缩为一个\(16\)位的二进制数,那么显然我们可以很好的存储状态。接下来要考虑的就是如何对当前状态进行拓展。首先我们易知操作世界上只有两种,一是交换左右棋子,二是交换上下棋子,我们只需要用位运算取出每一位的颜色并且判断与之交换的棋子颜色是否相同,再尝试进行两种交换判是否出现过即可。
代码
#include <bits/stdc++.h>
using namespace std;
bool vis[70000];
int step[70000];
queue<int> q;
int main()
{
int a=0,b=0;
char s[5];
for(int i=1;i<=4;i++)
{
scanf(" %s",s);
for(int j=0;j<4;j++)
a=a*2+s[j]-'0';
}
for(int i=1;i<=4;i++)
{
scanf(" %s",s);
for(int j=0;j<4;j++)
b=b*2+s[j]-'0';
}
q.push(a);
vis[a]=1;step[a]=0;
int p;
while(!q.empty())
{
int now=q.front();q.pop();
if(now==b)
{
printf("%d",step[now]);
break ;
}
for(int i=0;i<16;i++)
{
if(i%4!=0&&(now&(1<<i)^(now&(1<<i-1)))&&!vis[p=now^(1<<i)^(1<<i-1)])
{
vis[p]=1;
step[p]=step[now]+1;
q.push(p);
}
if(i<12&&(now&(1<<i))^(now&(1<<i+4))&&!vis[p=now^(1<<i)^(1<<i+4)])
{
vis[p]=1;
step[p]=step[now]+1;
q.push(p);
}
}
}
return 0;
}