题目描述###
在一个4*4的棋盘上有8个黑棋和8个白棋,当且仅当两个格子有公共边,这两个格子上的棋是相邻的。移动棋子的规则是交换相邻两个棋子。现在给出一个初始棋盘和一个最终棋盘,要求你找出一个最短的移动序列使初始棋盘变为最终棋盘。
Klux说:“这么简单的题目,我都会做!”
输入格式:###
第1到4行每行四个数字(1或者0),描述了初始棋盘
接着是一个空行
第6到9行每行四个数字,描述了最终棋盘
输出格式:###
输出只有一行是一个整数n,表示最少的移动步数。
输入样例#1:
1111
0000
1110
0010
1010
0101
1010
0101
输出样例#1:
4
解题思路###
BFS+位运算。
由于要求最小步数可以看出BFS的基本框架,但是如果用矩阵存储状态的话太耗费空间而且很慢,注意到每个格子的状态非0即1而且总格子数目为16所以可以用二进制的方法存储状态,相应判断,转移,判重。
注意这里面将棋盘转换成二进制序列的时候,如何计算序列上的值在原4*4棋盘上的位置,以及使用异或运算去生成相邻格子交换后的新棋盘状态对应的二进制序列也是本题特色。
最后,由于是交换相邻的格子,理论上格子和上下左右四个方向都可以互换,但是显然对于每个格子这样枚举互换存在大量的重复,本质上对于每个格子从上至下、从左到右只需要让他往右和往下和相邻的格子尝试互换即可。
#include<iostream>
#include<queue>
#define FOR(a,b,c) for(int a=(b);a<(c);a++)
using namespace std;
const int maxn = 16;
struct Node{ // 结构体存棋盘的二进制序列和步数
int num,d;
};
int A,B;
int vis[100000];
void BFS() {
queue<Node> q;
q.push((Node){A,0});
while(!q.empty())
{
Node u=q.front(); q.pop();
int tmp=u.num;
if(tmp==B) { cout<<u.d; return ; }
for(int i=15;i>=0;i--) // 棋盘对应的二进制序列,从高到低依次枚举每个位置
{
int x=(15-i)/4,y=(15-i)%4,w=1<<i,z; //计算该位置在棋盘上的x和y坐标值
if(y<3 && (tmp&(1<<i))!=(tmp&(1<<i-1))) //向右交换,二进制序列的i和i-1交换
{
z=1<<i-1;
if(!vis[tmp^z^w]) {
vis[tmp^z^w]=1;
q.push((Node){tmp^z^w,u.d+1});
}
}
if(x<3 && (tmp&(1<<i))!=(tmp&(1<<i-4))) //向下交换,二进制序列的i和i-4交换
{
z=1<<i-4;
if(!vis[tmp^z^w]) {
vis[tmp^z^w]=1;
q.push((Node){tmp^z^w,u.d+1});
}
}
}
}
}
int main()
{
char c;
for(int i=15;i>=0;i--) {
cin>>c;
if(c!='0') A += 1<<i;
}
for(int i=15;i>=0;i--) {
cin>>c;
if(c!='0') B += 1<<i;
}
if(A==B) cout<<0;
else BFS();
}