题目
题目链接:https://www.luogu.com.cn/problem/P4576
一个\(n*n(n\geq 2)\)棋盘上有黑白棋子各一枚。游戏者A和B轮流移动棋子,A先走。
- A的移动规则:只能移动白棋子。可以往上下左右四个方向之一移动一格。
- B的移动规则:只能移动黑棋子。可以往上下左右四个方向之一移动一格或者两格。
和通常的“吃子”规则一样,当某游戏者把自己的棋子移动到对方棋子所在的格子时,他就赢了。
两个游戏者都很聪明,当可以获胜时会尽快获胜,只能输掉的时候会尽量拖延时间。你的任务是判断谁会赢,需要多少回合。
比如\(n=2\),白棋子在\((1,1)\),黑棋子在\((2,2)\),那么虽然A有两种走法,第二个回合B总能取胜。
思路:
首先明显的一点就是,不存在平局,且A必胜当且仅当最开始白子与黑子就相邻。
如果不是,那么就是B必胜。此时我们只需要让B尽快获胜,A尽量拖时间即可。
设\(f[i][j][k][l][dep]\)表示白子在\((i,j)\),黑子在\((k,l)\),已经走了\(dep\)步时,黑子获胜需要走的最少步数。
那么白子从上下左右四个方向转移而来,取\(max\)值;黑子从上下左右上上下下左左右右(?)转移而来,取\(min\)值即可。
记搜即可。
那么边界是多少呢?
这里给出一种比较偷鸡的方法(
我们可以手动二分\(dep\)的最大值\(maxn\),如果\(dep>maxn\)就不再搜索下去。
枚举白子和黑子的每一个起点,然后用\(ans\)记录在搜索深度不超过\(maxn\)的情况下的\(max(solve(i,j,k,l))\),其中\(solve(i,j,k,l)\)为当白点在\((i,j)\),黑点在\((k,l)\)时,黑子获胜的最少步数。
如果\(ans=maxn+1\),那么就说明深度为\(maxn\)不够,否则已经足够。
实测答案最大为\(58\),所以搜索到\(60\)层就够了。
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=25,MAXN=90,Inf=1e9;
const int dx[]={0,0,0,-1,1,0,0,-2,2};
const int dy[]={0,1,-1,0,0,-2,2,0,0};
int n,sx,sy,tx,ty,f[N][N][N][N][MAXN];
int dfs(int x,int y,int xx,int yy,int dep)
{
if (xx==x && yy==y) return (dep&1) ? Inf : 0;
if (dep>60) return (dep&1) ? 0 : Inf;
if (f[x][y][xx][yy][dep]) return f[x][y][xx][yy][dep];
if (dep&1)
{
f[x][y][xx][yy][dep]=Inf;
for (register int i=1;i<=8;i++)
if (xx+dx[i]>=1 && xx+dx[i]<=n && yy+dy[i]>=1 && yy+dy[i]<=n)
f[x][y][xx][yy][dep]=min(f[x][y][xx][yy][dep],dfs(x,y,xx+dx[i],yy+dy[i],dep+1)+1);
}
else
for (register int i=1;i<=4;i++)
if (x+dx[i]>=1 && x+dx[i]<=n && y+dy[i]>=1 && y+dy[i]<=n)
f[x][y][xx][yy][dep]=max(f[x][y][xx][yy][dep],dfs(x+dx[i],y+dy[i],xx,yy,dep+1)+1);
return f[x][y][xx][yy][dep];
}
int main()
{
scanf("%d%d%d%d%d",&n,&sx,&sy,&tx,&ty);
for (register int i=1;i<=4;i++)
if (sx+dx[i]==tx && sy+dy[i]==ty) return !printf("WHITE 1");
printf("BLACK %d",dfs(sx,sy,tx,ty,0));
return 0;
}