題目連接: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";
}
}