题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=5754
【题意】B和G,在一个N*M的棋盘上下棋,棋子初始位置再(1,1),目标是走到(n,m)。棋子移动的位置必须是在其右下方向,及移动后的位置(x',y')相对原位置(x,y),x'≥x,y'≥y,同时棋子不能超出棋盘界限。有4种棋子,国王,马,车,皇后。棋子的移动规则同国际象棋,及国王可以向四周8个格子移动一格,题中可以向右下3个格子移动;马可以走日字,题中可以向右下两个位置移动;车可以直线无限格移动,题中可以向下和右移动无限格;皇后可以直线和斜线无限格移动,题中可以向下,右,右下三个方向无限格移动。每次给定棋子类型和棋盘大小,两人轮流移动棋子,谁先将棋子移动至(n,m)谁赢,棋子无法移动至(n,m)为平局。
【分析】由于每次只进行一个游戏,采用sg值的思想,区别为必败态标记0,必胜态标记1,平局标记-1。预处理出1000*1000棋盘四种棋子的所有解。国王:利用&操作判断三个后继是否存在0,存在则该格子是必胜态填1,否则为必败态填0;马:由于马存在平局的可能,判断马的两个后继是否存在0,存在则该格子为必胜态填1,否则判断是否存在-1,存在该格子为平局态填-1,都为1则该格子为必败态填0;车:简单找一下规律可以发现只有行列相等的时候是必败态填0,其他均为必胜态填1;皇后:比赛时通过打表找到的规律,由于对称,以i<j举例。以(0,0)为终点的话,下面的点是(1,2),(3,5),(4,7)。规律每次i=i+1,若i的数之前未出现过,j=j+2,该格子为必败态填0;若i出现过,j=j+1,不进行处理继续循环。比赛结束被告知皇后就是是威佐夫博弈=.=||,具体是什么不知道可以百度一下。循环结束后所有非0的格子均为必胜态填1。
【代码】
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
#define LL long long
int sg[1010][1010][5];
vector<int> v[5];
int kx[3]={0,-1,-1},ky[3]={-1,0,-1};
int nx[2]={-1,-2},ny[2]={-2,-1};
int re[1010];
void init(){
memset(sg,-1,sizeof(sg));
for(int i=1;i<=4;++i)
sg[0][0][i]=0;
int tmp=0;
for(int i=1;i<=1000 && tmp<=1000;++i){
if(re[i]){
tmp++;
continue;
}
tmp+=2;
re[i]=re[tmp]=1;
sg[i][tmp][4]=sg[tmp][i][4]=0;
}
for(int i=0;i<=1000;++i)
for(int j=0;j<=1000;++j){
if(i==0 && j==0)
continue;
int tmp=1;
for(int k=0;k<3;++k)
if(i+kx[k]>=0 && j+ky[k]>=0 && sg[i+kx[k]][j+ky[k]][1]!=-1)
tmp&=sg[i+kx[k]][j+ky[k]][1];
sg[i][j][1]=(!tmp);
if(i==j)
sg[i][j][2]=0;
else
sg[i][j][2]=1;
tmp=1;
int fla=0,fla2=0;
for(int k=0;k<2;++k)
if(i+nx[k]>=0 && j+ny[k]>=0){
if(sg[i+nx[k]][j+ny[k]][3]!=-1){
tmp&=sg[i+nx[k]][j+ny[k]][3];
fla=1;
}
else
fla2=1;
}
if(fla){
if(tmp){
if(fla2)
sg[i][j][3]=-1;
else
sg[i][j][3]=0;
}
else
sg[i][j][3]=1;
}
if(sg[i][j][4]!=0)
sg[i][j][4]=1;
}
}
int main(){
init();
int T,t,n,m;
cin>>T;
while(T--){
scanf("%d %d %d",&t,&n,&m);
n--;
m--;
if(sg[n][m][t]==1){
cout<<"B\n";
continue;
}
if(sg[n][m][t]==0){
cout<<"G\n";
continue;
}
cout<<"D\n";
}
}