题目大意
在一个 N∗N(N≥2) 的棋盘上有一颗白棋子和一颗黑棋子。白棋子每一回合能向上下左右走一步,黑棋能向上下左右走一步或者2步(每一回合只能向一个方向走)。若一个棋子覆盖了另外一个的话,他就赢了。问你在最优方案下,谁会赢。当然,每个棋子都会用最优策略,也就是说,若一个人知道他一定会输,他会尽量延迟结束。否则他会尽快使自己获得胜利。白棋先走
两个棋子的初始位置会给出。询问你谁会胜利,并且需要多少回合。
N≤20
解题思路
首先我们发现若一开始不是白棋就在黑子隔壁,它可以一步吃掉黑棋的话,黑棋绝对会获得胜利。
设最终的答案为 Ans .我们可以得到 Ans≤4∗N
证明
设白棋坐标为
(r,c)
,黑棋坐标为
(x,y)
不妨假设
r≤x,c≤y
其他情况是可以通过旋转这个棋盘而得到的。
我们设白棋走一回合,黑棋走一回合总称为一次。
我们考虑一次总共可能出现的情况。
可以发现的是,每走一次,黑棋和白棋之间的曼哈顿距离都会减少1。
又因为两个点初始的曼哈顿距离最大为 2N .所以我们最多会走 2N 次。总共就是 4N 个回合。
得证。
知道我们的 Ans≤4N 后,这题就变得简单了。
我们设 (dep,r,c,x,y,ctr) 为一个状态,表示从初始状态开始,走了 dep 个回合,当前白点在 (r,c) ,黑点在 (x,y) ,当前是由 ctr 这个点在做。若为0表示白点先手,1表示黑点先手。
设
Fstatus
若
ctr=0
,则表示在
status
这个状态时,黑棋至多要多少回合才能赢得游戏。
若
ctr=1
,则表示黑棋至少多少回合赢得游戏。
那么 Fstatus 的转移是很简单的。
若
ctr=0
,
Fstatus=max(FNextStatus)+1
若
ctr=1,Fstatus=min(FNextStatus)+1
我们这里可以用记忆化搜索来做。
而且若当前 status 的 dep>4∗N 时,这个状态其实是没有用的,因为我们已经知道了我们最优解肯定是 ≤4∗N 的。
复杂度是 O(8∗N5)
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 25;
const int xy[4][2] = {{1,0},{0,1},{-1,0},{0,-1}};
const int x2[4][2] = {{2,0},{0,2},{-2,0},{0,-2}};
int F[105][MAXN][MAXN][MAXN][MAXN][2],N,step;
int Dfs(int r1,int c1,int r2,int c2,int ctr,int dep)
{
if (r1 == r2 && c1 == c2)
{
if (ctr == 0) return F[dep][r1][c1][r2][c2][ctr] = 0;
return F[dep][r1][c1][r2][c2][ctr] = -2;
}
if (dep > step) return -2;
if (F[dep][r1][c1][r2][c2][ctr] != -1) return F[dep][r1][c1][r2][c2][ctr];
if (!ctr)
{
int pt = 0;
for(int i = 0;i < 4;i ++)
{
int nx = r1 + xy[i][0],ny = c1 + xy[i][1];
if (nx && ny && nx <= N && ny <= N)
{
int tmp = Dfs(nx,ny,r2,c2,!ctr,dep + 1);
if (tmp == -2) {pt = -2;break;}
pt = max(pt,tmp + 1);
}
}
return F[dep][r1][c1][r2][c2][ctr] = pt;
} else
{
int pt = (1 << 30);
for(int i = 0;i < 4;i ++)
{
int nx = r2 + xy[i][0],ny = c2 + xy[i][1];
if (nx && ny && nx <= N && ny <= N)
{
int tmp = Dfs(r1,c1,nx,ny,!ctr,dep + 1);
if (tmp == -2) continue;
pt = min(pt,tmp + 1);
}
}
for(int i = 0;i < 4;i ++)
{
int nx = r2 + x2[i][0],ny = c2 + x2[i][1];
if (nx && ny && nx <= N && ny <= N)
{
int tmp = Dfs(r1,c1,nx,ny,!ctr,dep + 1);
if (tmp == -2) continue;
pt = min(pt,tmp + 1);
}
}
if (pt == (1 << 30)) pt = -2;
return F[dep][r1][c1][r2][c2][ctr] = pt;
}
}
int main()
{
int r1,c1,r2,c2;
scanf("%d%d%d%d%d", &N, &r1, &c1, &r2, &c2);
if (abs(r1 - r2) + abs(c1 - c2) <= 1) printf("WHITE 1\n"); else
{
step = 4 * N;
{
for(int j = 0;j <= step;j ++) memset(F[j],255,sizeof F[j]);
Dfs(r1,c1,r2,c2,0,0);
if (F[0][r1][c1][r2][c2][0] >= 0) {printf("BLACK %d\n", F[0][r1][c1][r2][c2][0]);return 0;}
}
printf("DRAW\n");
}
}