[Wikioi 1004]四子连棋(复习)

在一个4*4的棋盘上摆放了14颗棋子,其中有7颗白色棋子,7颗黑色棋子,有两个空白地带,任何一颗黑白棋子都可以向上下左右四个方向移动到相邻的空格,这叫行棋一步,黑白双方交替走棋,任意一方可以先走,如果某个时刻使得任意一种颜色的棋子形成四个一线(包括斜线),这样的状态为目标棋局。

 
 

 

从文件中读入一个4*4的初始棋局,黑棋子用B表示,白棋子用W表示,空格地带用O表示。

用最少的步数移动到目标棋局的步数。

BWBO
WBWB
BWBW
WBWO

5

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;
}


 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值