BZOJ 3106 棋盘游戏
Description
一个\(n \times n(n \le 2)\)棋盘上有黑白棋子各一枚。游戏者A和B轮流移动棋子,A先走。
A的移动规则:只能移动白棋子。可以往上下左右四个方向之一移动一格。
B的移动规则:只能移动黑棋子。可以往上下左右四个方向之一移动一格或者两格。
和通常的“吃子”规则一样,当某游戏者把自己的棋子移动到对方棋子所在的格子时,他就赢了。两个游戏者都很聪明,当可以获胜时会尽快获胜,只能输掉的时候会尽量拖延时间。你的任务是判断谁会赢,需要多少回合。
比如\(n=2\),白棋子在\((1,1)\),黑棋子在\((2,2)\),那么虽然A有两种走法,第二个回合B总能取胜。
Input
输入仅一行,包含五个整数\(n, r_{1}, c_{1}, r_{2}, c_{2}\),即棋盘大小和棋子位置。白色棋子在\((r_{1},c_{1})\),黑色棋子在\((r_{2},c_{2})(1 \le r_{1},c_{1},r_{2},c_{2} \le n)\)。黑白棋子的位置保证不相同。
Output
输出仅一行,即游戏结果。如果A获胜,输出WHITE \(x\);如果B获胜,输出BLACK \(x\);如果二者都没有必胜策略,输出DRAW。
Sample Input
2 1 1 2 2
Sample Output
BLACK 2
HINT
\(n \le 20\)
首先有个结论可以判胜负:
如果A与B只相差一格,那么A一定获胜;否则B一定获胜。那个平局只是来卖个萌的。(B走的快一些)
然后就是求合法的步数了,这个是CLB告诉我的极大极小搜索。
我开始裸搜果断TLE,加了记忆化,果断WA,没怎么打过,怎么调也调不对,最后还是参照了别人的程序长了见识。
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
using namespace std;
#define inf (1<<30)
#define lim (3*n+2)
#define maxn (25)
const int xx[] = {0,1,0,-1},yy[] = {1,0,-1,0};
int n,X1,X2,Y1,Y2,ans; int vis[maxn][maxn][maxn][maxn][maxn*3][2];
inline bool okay(int x,int y) { return x > 0&&x<=n&&y > 0&&y<=n; }
inline int dfs(bool now,int a1,int b1,int a2,int b2,int step)
{
if (step > lim) return inf;
if (vis[a1][b1][a2][b2][step][now]) return vis[a1][b1][a2][b2][step][now];
else if (a1 == a2&&b1 == b2)
{
if (now) return inf;
return 0;
}
int ret;
if (!now)
{
ret = 0;
for (int i = 0;i < 4;++i)
if (okay(a1+xx[i],b1+yy[i]))
ret = max(dfs(now^1,a1+xx[i],b1+yy[i],a2,b2,step+1),ret);
}
else
{
ret = inf;
for (int j = 1;j <= 2;++j)
for (int i = 0;i < 4;++i)
if (okay(a2+j*xx[i],b2+j*yy[i]))
ret = min(dfs(now^1,a1,b1,a2+j*xx[i],b2+j*yy[i],step+1),ret);
}
return vis[a1][b1][a2][b2][step][now] = ++ret;
}
int main()
{
freopen("3106.in","r",stdin);
freopen("3106.out","w",stdout);
scanf("%d %d %d %d %d\n",&n,&X1,&Y1,&X2,&Y2);
if (abs(X1-X2)+abs(Y1-Y2) == 1) printf("WHITE 1");
else
{
printf("BLACK ");
printf("%d",dfs(0,X1,Y1,X2,Y2,0));
}
fclose(stdin); fclose(stdout);
return 0;
}
高考结束,重新回归。