题目
题目描述
在一个4*4的方框内摆放了若干个相同的玩具,某人想将这些玩具重新摆放成为他心中理想的状态,规定移动时只能将玩具向上下左右四个方向移动,并且移动的位置不能有玩具,请你用最少的移动次数将初始的玩具状态移动到某人心中的目标状态。
输入格式
前4行表示玩具的初始状态,每行4个数字1或0,1表示方格中放置了玩具,0表示没有放置玩具。接着是一个空行。接下来4行表示玩具的目标状态,每行4个数字1或0,意义同上。
输出格式
一个整数,所需要的最少移动次数。
样例输入
1111
0000
1110
0010
1010
0101
1010
0101
样例输出
4
分析
发现目标状态和初始状态都很明确,使用双向广搜。
我把状态存成整数
N
N
,然后双向搜索。
直观地对比一下单向搜索和双向搜索:
代码
又丑又恶心的代码。
#include<queue>
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAXN 65536
#define PII pair<int,int>
const int dir[5]={0,1,-1,4,-4};
const int Pow[20]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536};
int vis[2][MAXN+5];
int R(int i){return (i-1)/4+1;}
int C(int i){return (i-1)%4+1;}
//不好解释,自己想想就明白了
int BFS(int S,int T){
queue<PII > Q[2];
Q[0].push(make_pair(S,0));
Q[1].push(make_pair(T,0));
while(!Q[0].empty()&&!Q[1].empty()){
PII Now;
for(int k=0;k<=1;k++){//做两遍,分别对应前半部分的搜索和后半部分的搜索
Now=Q[k].front();Q[k].pop();
for(int i=1;i<=16;i++)
if(Now.first&(1<<(i-1)))//这个位置有玩具
for(int j=1;j<=4;j++){
int New=i+dir[j];
if(New<1||New>16) continue;
if(R(i)!=R(New)&&C(i)!=C(New)) continue;//这个很重要,因为有可能从(2,4)走到(3,1),这样是不行的
if(Now.first&(1<<(New-1))) continue;//要走到的位置有玩具
PII tmp(Now.first-(1<<(i-1))+(1<<(New-1)),Now.second+1);
if(vis[k][tmp.first]) continue;
if(vis[!k][tmp.first]) return tmp.second+vis[!k][tmp.first];
vis[k][tmp.first]=tmp.second;
Q[k].push(tmp);
}
}
}
return -1;
}
int main(){
freopen("movetoy.in", "r" ,stdin);
freopen("movetoy.out","w",stdout);
int S=0,T=0;
for(int i=1;i<=16;i++){
S+=(getchar()-'0')*Pow[i-1];
if(i%4==0) getchar();
}
getchar();
for(int i=1;i<=16;i++){
T+=(getchar()-'0')*Pow[i-1];
if(i%4==0) getchar();
}
if(S==T){//不加这个它会输出2
printf("0");
return 0;
}
int Ans=BFS(S,T);
printf("%d",Ans);
}