Description
在一个
4
×
4
4×4
4×4的棋盘上有8个黑棋和8个白棋,当且仅当两个格子有公共边,这两个格子上的棋是相邻的。
移动棋子的规则是交换相邻两个棋子。
给出一个初始棋盘和一个最终棋盘,请找出一个最短的移动序列使初始棋盘变为最终棋盘。
Input
前四行,每行4个数字(1或者0),描述了初始棋盘;
接着是一个空行;
第六到第九行,每行4个数字(1或者0),描述了最终棋盘。
Output
输出一行一个整数表示最少步数。
- 前置技能 :二进制基本操作、bfs
- 因为棋盘由01表示,那么我们将原有棋盘转成一串16位二进制数。则原题可抽象为将一个数 A A A转化成另一个数 B B B,求A转到B最少次数显然可以用BFS + 二进制去暴力,
- 交换的规则我们也可以做优化,只需确定两个方向即可(左、上)或者(右下),这样便不会引起多余的交换
#include<bits/stdc++.h>
#include<stdio.h>
#include<string.h>
#define maxn 200005
#define INF 0x3f3f3f3f
#define mst(a) memset(a,0,sizeof a)
#define ll long long
using namespace std;
//题号:棋盘
struct node{int num,time;};
bool vis[1<<20];
void read(int& num)
{
int t;
for(int i=3; i>=0; i--)
{
cin >>t;
for(int j = 3; j>=0; j--)
{
int k = t%10;
t/=10;
if(k)num+= 1<<(i*4+j);
}
}
}
int bfs(int st,int end)
{
node st1;
st1.num = st, st1.time = 0;
queue<node> q;
q.push(st1);
while(!q.empty())
{
node now = q.front();
q.pop();
int num = now.num;
if(num == end)return now.time;
for(int i = 0; i<16; i++) //左走
{
int x = (15-i)%4, y = (15-i)/4; //编号转坐标
int k = 1<<i,k1 = 1<<(i+1); //k为当前点 ,k1为交换点
if(x>0 && (num&k)!=(num&k1))
{
if(!vis[num^k^k1])
{
vis[num^k^k1] = 1;
node t_node;
t_node.num = num^k^k1;
t_node.time = now.time+1;
q.push(t_node);
}
}
}
for(int i = 0; i<16; i++) //上走
{
int x = (15-i)%4, y = (15-i)/4;
int k = 1<<i,k1 = 1<<(i+4); //k为当前点 ,k1为交换点
if(y>0 && (num&k)!=(num&k1))
{
if(!vis[num^k^k1])
{
vis[num^k^k1] = 1;
node t_node;
t_node.num = num^k^k1;
t_node.time = now.time+1;
q.push(t_node);
}
}
}
}
}
int main()
{
int st = 0, end = 0, temp;
read(st); read(end);
cout << bfs(st,end)<<endl;
return 0;
}