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
0101

Sample 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一下就行。。。实在想建图的慢慢建吧哈哈哈哈哈哈哈
View Code

 

posted @ 2016-10-14 15:42  Alpar  阅读(229)  评论(0编辑  收藏  举报