BZOJ 1054题解 BFS暴力求解
BZOJ 1054题解 BFS暴力求解
1054: [HAOI2008]移动玩具
Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 1884 Solved: 1033
[Submit][Status][Discuss]Description
在一个4*4的方框内摆放了若干个相同的玩具,某人想将这些玩具重新摆放成为他心中理想的状态,规定移动时只能将玩具向上下左右四个方向移动,并且移动的位置不能有玩具,请你用最少的移动次数将初始的玩具状态移动到某人心中的目标状态。Input
前4行表示玩具的初始状态,每行4个数字1或0,1表示方格中放置了玩具,0表示没有放置玩具。接着是一个空行。接下来4行表示玩具的目标状态,每行4个数字1或0,意义同上。Output
一个整数,所需要的最少移动次数。
Sample Input
1111
0000
1110
0010
1010
0101
1010
0101Sample Output
4
这道题第一眼看过去就跟codevs的四子连棋很像,只不过这道题的空白点数量不确定。对于求最短步数的搜索我们可以迭代加深搜索或者bfs,但针对这道题迭代的话过于麻烦,那么我们就直暴力bfs+判重。对于判重,我们可以分析下棋盘,棋盘只有4x4十六个点,每个点只有0,1两种状态,我们可以将每种棋盘的状况用一个数字存下来,这个数字不会超过 216 (大概六万多一点),然后用一个status数组判断这种情况是否访问过。
#include<cstdio> #include<deque> using namespace std; int status[70000]; //用来判重 int nums,nume; struct STATUS{ int t[5][5]; int step; }sts[2]; //用结构体存棋盘和步数 int dx[]={ 1, 0,-1, 0}; int dy[]={ 0, 1, 0,-1}; deque<STATUS>q; //队列queue的内部其实是deque...直接用deque可能快上那么一丢丢 bool check(STATUS temp){ //判重函数 int num=0; for(int i=1;i<=4;i++) for(int j=1;j<=4;j++) num=(num<<1)+temp.t[i][j]; //转十进制 if(status[num]==1)return false; else return status[num]=true; } bool cmp(STATUS a,STATUS b){ //比较两个棋盘是否相等 for(int i=1;i<=4;i++) for(int j=1;j<=4;j++){ if(a.t[i][j]!=b.t[i][j])return false; } return true; } void search(){ sts[0].step=0; q.push_back(sts[0]); while(!q.empty()){ STATUS curr=q.front();q.pop_front(); for(int i=1;i<=4;i++) for(int j=1;j<=4;j++){ if(curr.t[i][j]==1){ for(int k=0;k<4;k++){ int kx=dx[k]+i; int ky=dy[k]+j; if(kx>0&&ky>0&&ky<5&&kx<5&&curr.t[kx][ky]==0){ STATUS copy=curr; swap(copy.t[i][j],copy.t[kx][ky]); ++copy.step; if(check(copy)){ q.push_back(copy); if(cmp(copy,sts[1]))return ;//如果达到目标棋盘则直接return
} } } } } } } void input(){ char c[6]; nums=0;nume=0; for(int i=1;i<=4;i++){ scanf("%s",c); scanf("\n"); for(int j=0;j<4;j++){ if(c[j]=='1')sts[0].t[i][j+1]=1; nums=(nums<<1)+c[j]-'0'; } } status[nums]=1; for(int i=1;i<=4;i++){ scanf("%s",c); if(i!=4)scanf("\n"); for(int j=0;j<4;j++){ if(c[j]=='1')sts[1].t[i][j+1]=1; nume=(nume<<1)+c[j]-'0'; } } status[nume]=0; } int main(){ input(); if(cmp(sts[1],sts[0])){ //特判一下起始棋盘和目标棋盘是否是一样的,不然会迷之WA printf("0"); return 0; } search(); printf("%d",q.back().step); }
其实这道题还有别的思路,我们可以将每种情况枚举出来,然后建图。 读入时,用cnt记录棋盘中1的数目,再枚举一下所有有cnt个1的棋盘,每个棋盘用它的二进制数字作为序号来建点。如果两个棋盘只有1步之差,那么我们给他们连上一个权值为1的边。连边完成后,我们就可以用spfa或dijkstra来求起始棋盘到目标棋盘的最短路。这个最短路就是最短步数。
伪代码如下
那个什么,建图连边太麻烦,我懒得写了。。。这道题明显就很水,随便bfs一下就行。。。实在想建图的慢慢建吧哈哈哈哈哈哈哈
。