题意
一个n*n(n>=2)棋盘上有黑白棋子各一枚。游戏者A和B轮流移动棋子,A先走。
A的移动规则:只能移动白棋子。可以往上下左右四个方向之一移动一格。
B的移动规则:只能移动黑棋子。可以往上下左右四个方向之一移动一格或者两格。
和通常的“吃子”规则一样,当某游戏者把自己的棋子移动到对方棋子所在的格子时,他就赢了。两个游戏者都很聪明,当可以获胜时会尽快获胜,只能输掉的时候会尽量拖延时间。你的任务是判断谁会赢,需要多少回合。
n≤20
分析
若一开始白棋在黑棋周围一格,则白棋可以获胜,否则白棋显然不可能获胜。
然后我们有一个非常重要的结论,黑棋获胜的步数
Ans≤4∗n
证明详见http://blog.csdn.net/PhilipsWeng/article/details/47841649
这样我们可以限深记忆化搜索,步数
dep>4∗n
的状态我们可以不做。
设
f[dep][x1][y1][x2][y2]
为过了
dep
个回合,白棋在
(x1,y1)
,黑棋在
(x2,y2)
的状态。对于当前是白棋走(dep是偶数),它肯定不能获胜,则它肯定会选择黑棋最慢获胜的后继状态,状态记录的是最久还需几个回合黑棋才会获胜;反之,当前是黑棋走(dep是奇数),它会选择它最快获胜的后继状态,该状态为最快还需几个回合黑棋就会获胜。
那么转移方程就很容易推出了。
代码
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 22;
const int flag[4][2] = {{1,0},{0,1},{-1,0},{0,-1}};
int f[4 * N][N][N][N][N],n,lim,x1,y1,x2,y2;
int dfs(int dep,int x1,int y1,int x2,int y2) {
if (f[dep][x1][y1][x2][y2] != -1) return f[dep][x1][y1][x2][y2];
if (dep > lim) return -2;
if (x1 == x2 && y1 == y2) {
if (dep & 1) return -2;
return 0;
}
if (dep & 1) {
int re = (1 << 30);
for (int i = 0;i < 4;i ++) {
int x = x2 + flag[i][0],y = y2 + flag[i][1];
if (x < 1 || y < 1 || x > n || y > n) continue;
int cur = dfs(dep + 1,x1,y1,x,y);
if (cur == -2) continue;
re = min(re,cur + 1);
}
for (int i = 0;i < 4;i ++) {
int x = x2 + 2 * flag[i][0],y = y2 + flag[i][1] * 2;
if (x < 1 || y < 1 || x > n || y > n) continue;
int cur = dfs(dep + 1,x1,y1,x,y);
if (cur == -2) continue;
re = min(re,cur + 1);
}
if (re == (1 << 30)) re = -2;
return f[dep][x1][y1][x2][y2] = re;
}
else {
int re = 0;
for (int i = 0;i < 4;i ++) {
int x = x1 + flag[i][0],y = y1 + flag[i][1];
if (x < 1 || y < 1 || x > n || y > n) continue;
int cur = dfs(dep + 1,x,y,x2,y2);
if (cur == -2) return f[dep][x1][y1][x2][y2] = -2;
re = max(re,cur + 1);
}
return f[dep][x1][y1][x2][y2] = re;
}
}
int main() {
scanf("%d%d%d%d%d",&n,&x1,&y1,&x2,&y2);
if (abs(x1 - x2) + abs(y1 - y2) <= 1) printf("WHITE 1\n");
else{
lim = 4 * n;
memset(f,255,sizeof(f));
int ans = dfs(0,x1,y1,x2,y2);
if (ans >= 0) printf("BLACK %d\n",ans);
else printf("DRAW\n");
}
}