题意
一个n*n(n>=2)棋盘上有黑白棋子各一枚。游戏者A和B轮流移动棋子,A先走。
-
A的移动规则:只能移动白棋子。可以往上下左右四个方向之一移动一格。
-
B的移动规则:只能移动黑棋子。可以往上下左右四个方向之一移动一格 或者两格。
和通常的“吃子”规则一样,当某游戏者把自己的棋子移动到对方棋子所在的格子时,他就赢了。
两个游戏者都很聪明,当可以获胜时会尽快获胜,只能输掉的时候会尽量拖延时间。你的任务是判断谁会赢,需要多少回合。
思路
首先考虑谁会赢。这很简单。
记 d = ∣ r 1 − r 2 ∣ + ∣ c 1 − c 2 ∣ d=|r1-r2|+|c1-c2| d=∣r1−r2∣+∣c1−c2∣为黑棋和白棋的曼哈顿距离。
首先假设黑棋没有特权,只能走一格。
那么当 d = 1 d=1 d=1时白棋胜。
当 d = 2 d=2 d=2市有两种情况:
- 两棋在一个 2 ∗ 2 2*2 2∗2的方格的两个角上,可以发现先手必输,因为先手只能把棋子移动到这个 2 ∗ 2 2*2 2∗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 n∗3,容易知道黑棋可以逼白棋跑不到离初始点 2 ∗ n 2*n 2∗n格的地方,所以上限就设成两棋都跑完对角线的步数。
代码
#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;
}