题目描述 Description
在一个4*4的棋盘上摆放了14颗棋子,其中有7颗白色棋子,7颗黑色棋子,有两个空白地带,任何一颗黑白棋子都可以向上下左右四个方向移动到相邻的空格,这叫行棋一步,黑白双方交替走棋,任意一方可以先走,如果某个时刻使得任意一种颜色的棋子形成四个一线(包括斜线),这样的状态为目标棋局。
● ○ ● ○ ● ○ ● ● ○ ● ○ ○ ● ○
输入描述 Input Description
从文件中读入一个4*4的初始棋局,黑棋子用B表示,白棋子用W表示,空格地带用O表示。
输出描述 Output Description
用最少的步数移动到目标棋局的步数。
样例输入 Sample Input
BWBO
WBWB
BWBW
WBWO
样例输出 Sample Output
5
数据范围及提示 Data Size & Hint
hi
#include <stdio.h>
#include <map>
#include <queue>
#include <stdlib.h>
using namespace std;
struct status
{
int step; //step=走的棋总步数
int hash; //哈希状态
int map[6][6]; //记录棋盘状态 map[i][j]=第i行第j列
int last; //last=上一回合下的棋的颜色,last=1表示黑棋,last=2表示白棋
}first; //first=没下棋之前的初始状态
queue <struct status> Q; //保存BFS每一层搜索结果的队列Q
map<int,bool>h[3]; //BFS判重map数组
int xx[]={1,-1,0,0},yy[]={0,0,1,-1};
int gethash(status tmp) //获取tmp棋盘的哈希值
{
int sum=0,i,j,k=1;
for(i=1;i<=4;i++)
{
for(j=1;j<=4;j++)
{
sum+=tmp.map[i][j]*k;
k*=3;
}
}
return sum;
}
int check(status tmp) //判断棋盘tmp是否达到目标要求,是则返回1,不是则返回0
{
int i,j,k,flag;
for(i=1;i<=4;i++) //检查每一横排是否有四连棋
{
flag=1;
for(j=1;j<4;j++)
if(tmp.map[i][j]!=tmp.map[i][j+1])
flag=0;
if(flag==1) return 1;
}
for(j=1;j<=4;j++) //检查每一竖列是否有四连棋
{
flag=1;
for(i=1;i<4;i++)
if(tmp.map[i][j]!=tmp.map[i+1][j])
flag=0;
if(flag==1) return 1;
}
if(tmp.map[1][1]==tmp.map[2][2]&&tmp.map[2][2]==tmp.map[3][3]&&tmp.map[3][3]==tmp.map[4][4]) //检查对角线上是否有四连棋
return 1;
if(tmp.map[1][4]==tmp.map[2][3]&&tmp.map[2][3]==tmp.map[3][2]&&tmp.map[3][2]==tmp.map[4][1]) //检查对角线上是否有四连棋
return 1;
return 0;
}
void move(status now,int x,int y,int k) //move(上一回合棋盘状态,棋盘空格x坐标,棋盘空格y坐标,移动方向k)
{
status tmp=now;
int tmpx=x+xx[k]; //tmpx=空格到达的目标棋格x坐标
int tmpy=y+yy[k]; //tmpy=空格到达的目标棋格y坐标
if(tmpx<1||tmpx>4) return; //目标棋格坐标越界,退出
if(tmpy<1||tmpy>4) return;
if(tmp.map[tmpx][tmpy]==tmp.last) return; //被移动的棋子不是我方颜色的,退出
tmp.last=3-tmp.last; //交换棋子颜色,这次下和上次不同的棋
swap(tmp.map[x][y],tmp.map[tmpx][tmpy]); //移动棋子
tmp.step++; //移动步数+1
tmp.hash=gethash(tmp);
if(check(tmp)) //移动后棋子到达目标状态,直接输出结果,退出程序
{
printf("%d\n",tmp.step);
exit(0);
}
if(!h[tmp.last][tmp.hash]) //该状态没有被访问过
{
h[tmp.last][tmp.hash]=1; //标记该状态访问过
Q.push(tmp); //将该状态的tmp入队
}
}
void bfs() //bfs广搜过程
{
first.hash=gethash(first);
first.last=1; //因为第一次下棋的首方不清楚,因此先将第一个是下黑棋的入队
Q.push(first);
first.last=2; //再将第一个是下白棋的入队
Q.push(first);
while(!Q.empty()) //当队列中还有棋盘状态没有搜索完(队列为空),继续搜索
{
status now;
int x1=-1,x2=-1,y1=-1,y2=-1,i,j,k;
now=Q.front(); //取出队首的棋盘状态now
Q.pop();
for(i=1;i<=4;i++)
{
for(j=1;j<=4;j++)
{
if(now.map[i][j]==0)
{
if(x1==-1&&x2==-1) //找到第一个空格
{
x1=i;
y1=j;
}
else //否则,第一个空格已经找到,现在找到了第二个空格
{
x2=i;
y2=j;
}
}
}
}
//这步棋有两种选择:
//1、移动第1个空格
//2、移动第2个空格
//分别进行四个方向的移动、状态入队操作
for(k=0;k<4;k++)
{
move(now,x1,y1,k); //移动第1个空格
move(now,x2,y2,k); //移动第2个空格
}
}
}
int main()
{
int i,j;
for(i=1;i<=4;i++)
{
char in[10];
scanf("%s",in);
for(j=1;j<=4;j++)
{
if(in[j-1]=='B')
first.map[i][j]=1;
else if(in[j-1]=='W')
first.map[i][j]=2;
else if(in[j-1]=='O')
first.map[i][j]=0;
}
}
bfs();
return 0;
}