【CQOI2013】棋盘游戏(搜索)

题意

一个n*n(n>=2)棋盘上有黑白棋子各一枚。游戏者A和B轮流移动棋子,A先走。

  • A的移动规则:只能移动白棋子。可以往上下左右四个方向之一移动一格。

  • B的移动规则:只能移动黑棋子。可以往上下左右四个方向之一移动一格 或者两格

和通常的“吃子”规则一样,当某游戏者把自己的棋子移动到对方棋子所在的格子时,他就赢了。

两个游戏者都很聪明,当可以获胜时会尽快获胜,只能输掉的时候会尽量拖延时间。你的任务是判断谁会赢,需要多少回合。

思路

首先考虑谁会赢。这很简单。

d = ∣ r 1 − r 2 ∣ + ∣ c 1 − c 2 ∣ d=|r1-r2|+|c1-c2| d=r1r2+c1c2为黑棋和白棋的曼哈顿距离。

首先假设黑棋没有特权,只能走一格。

那么当 d = 1 d=1 d=1时白棋胜。

d = 2 d=2 d=2市有两种情况:

  1. 两棋在一个 2 ∗ 2 2*2 22的方格的两个角上,可以发现先手必输,因为先手只能把棋子移动到这个 2 ∗ 2 2*2 22的方格外,否则下一步他就死了,而先手走一步,后手可以复制先手的这一步,再次形成这个局面,从而把先手逼到角落杀死。
  2. 在一条直线上。假设两棋在同一行上,先手在左边(仅仅是为了方便表述)。首先先手不能向右走。然后假如先手向上或者下走,那后手向左一步,达到上述局面1。所以先手只能向左走,直到被逼到边界。

因为两棋各走一步曼哈顿距离奇偶性不变,所以可以推广为 d d d为奇数白棋胜, d d d为偶数黑棋胜。

那么假如黑棋有可以走两步的特权,那他就可以改变 d d d奇偶性,保证自己获胜,除非 d = 1 d=1 d=1时白棋直接吃掉它。

然后考虑步数。因为状态少的可怜,可以直接爆搜。记 d p [ r 1 ] [ c 1 ] [ r 2 ] [ c 2 ] [ f i r ] [ d p t ] dp[r1][c1][r2][c2][fir][dpt] dp[r1][c1][r2][c2][fir][dpt] f i r = 0 / 1 fir=0/1 fir=0/1表示白棋走还是黑棋走, d p t dpt dpt表示当前已经走了的步数。

注意

搜索的时候,

注意 d p t dpt dpt这一维不能省,否则难以考虑走回原来状态的情况。

注意假如两棋重合,不一定返回0,假如时白棋吃掉了黑棋,则返回inf表示不行。

为了防止时间爆炸,设置搜索深度上限为 n ∗ 3 n*3 n3,容易知道黑棋可以逼白棋跑不到离初始点 2 ∗ n 2*n 2n格的地方,所以上限就设成两棋都跑完对角线的步数。

代码

#include<bits/stdc++.h>
using namespace std;
const int inf = 1e9 + 7;
const int N = 20 + 1;
const int dx[8] = {0, 0, -1, 1, 0, 0, -2, 2};
const int dy[8] = {1, -1, 0, 0, -2, 2, 0, 0};
int n, r1, c1, r2, c2;
int f[N*N*N*N][2][N*3];

inline int get_id(int r1, int c1, int r2, int c2)
{
	int d1 = (r1 - 1) * n + c1 - 1;
	int d2 = (r2 - 1) * n + c2 - 1;
	return d1 + n * n * d2;
}

inline int dis(int r1, int c1, int r2, int c2)
{
	return abs(r1 - r2) + abs(c1 - c2);
}

int r3, c3;
int dfs(int r1, int c1, int r2, int c2, bool fir, int dpt)
{
	if (r1 == r2 && c1 == c2) return (fir == 0 ? inf : 0);
	if (dpt > n * 3) return inf;
	int id = get_id(r1, c1, r2, c2), ans;
	if (f[id][fir][dpt] != -1) return f[id][fir][dpt];
	if (fir == 1){
		ans = 0;
		for (int ii = 0; ii < 4; ++ ii){
			r3 = r1 + dx[ii];
			c3 = c1 + dy[ii];
			if (r3 < 1 || r3 > n || c3 < 1 || c3 > n) continue;
			ans = max(ans, dfs(r3, c3, r2, c2, 0, dpt + 1) + 1);
		}
	}
	else{
		ans = inf;
		for (int ii = 0; ii < 8; ++ ii){
			r3 = r2 + dx[ii];
			c3 = c2 + dy[ii];
			if (r3 < 1 || r3 > n || c3 < 1 || c3 > n) continue;
			ans = min(ans, dfs(r1, c1, r3, c3, 1, dpt + 1) + 1);
		}
	}
	return f[id][fir][dpt] = ans;
}

int main()
{
	cin >> n >> r1 >> c1 >> r2 >> c2;
	if (dis(r1, c1, r2, c2) == 1){
		cout << "WHITE 1" << endl;
		return 0;
	}
	cout << "BLACK ";
	memset(f, -1, sizeof(f));
	cout << dfs(r1, c1, r2, c2, 1, 0) << endl;
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值