题目描述
给定一个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(216⋅16⋅4) 。
注:
222=4194304
link: http://acm.hpu.edu.cn/problem.php?id=1152
#pragma GCC optimize ("O2")
#include<stdio.h>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
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<<i;
}
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<<i) & V | 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;
}