[HPUOJ] 1152: 棋盘变换 [搜索]

题目描述

给定一个4*4的01棋盘,1代表棋子,0代表空格,棋子1每次可以移动到相邻上下左右四个位置的空格。然后再给定你目标棋盘,问你最少在多少步能把当前棋盘变成目标棋盘状态。

输入

第一行输入一个整数T,代表有T组测试数据。接下来给出只有0和1的4*4的当前棋盘和4*4的目标棋盘,中间有一个空行。

输出

输出一个整数表示最小的步数,若不能到达输出-1。

样例输入

1
0001
0011
1100
1111

1011
1101
0000
1101

样例输出

8

思路

港巨巨提供的思路:

把棋盘是 4×4 的,有 216 种状态,所以可以分别用数字表示出每个状态。用数字表示出了状态就可以对状态进行记忆了,从起始位置BFS跑一遍即可,复杂度 O(216164)

注: 222=4194304
link: http://acm.hpu.edu.cn/problem.php?id=1152

#pragma GCC optimize ("O2")
#include
#include
#include
#include
#include
#include
#include

using namespace std;

int s,t,dp[1<<17];
const int ox[]={0,0,1,-1};
const int oy[]={1,-1,0,0};

int get(int &x){
    x=0;int z,cnt=0;
    for(int i=0,z;i<16;++i){
        scanf("%1d",&z);
        if(z) ++cnt,x+=1<return cnt;
}

bool ex(int v,int x,int y){return (v>>(x*4+y))&1;}

/*
void print(int n){
    for(int i=0;i<16;++i){
        if(i!=0&&i%4==0) putchar('\n');
        printf("%d",n>>i&1);
    }
    putchar('\n');
}
*/

void work(){
    memset(dp,-1,sizeof(dp));
    queue<int> que;
    que.push(s);
    dp[s]=0;
    while(!que.empty()){
        int V=que.front();que.pop();
        if(V==t) break;
    //  printf("[%d] ----\n",dp[V]);
    //  print(V);
        for(int i=0;i<16;++i){
            if(V>>i&1){
                int X=i/4,Y=i%4;
                for(int j=0;j<4;++j){
                    int x=X+ox[j];
                    int y=Y+oy[j];
                    if(0<=x&&x<4&&0<=y&&y<4&&!ex(V,x,y)){
                        int v=~(1<1<<(4*x+y);
                        if(dp[v]!=-1) continue;
                        dp[v]=dp[V]+1;
                        que.push(v);
                    }
                }
            }
        }
    }
    while(!que.empty()) que.pop();
    printf("%d\n",dp[t]);
}

int main()
{
    int T,z;
    scanf("%d",&T);
    while(T--){
        if(get(s)!=get(t)) puts("-1");
        else work();
    }
    return 0;
}

你可能感兴趣的:(搜索,bfs)