题目链接:HDU 5754
题面:
题意:
给定N*M的棋盘,一共4颗棋子,每次一种1颗棋子,从左上角出发,到右下角的人获胜。
解题:
1.王的走法是向下向右1格,或同时向下向右1格,即(x+1,y)/(x,y+1)/)(x+1,y+1)三种走法,递推推一下即可。一个节点的后继为必败态,那么它为必胜态,如果一个节点的后继全都是必胜态,那么它就是必败态,按剩余步数,从小到大递推一下即可。
2.车的走法是横向或竖向任意格,那么在对角线上必输,因为先手走什么策略,后手模仿即可,反之n!=m,先手只要使对方到对角线上即必胜。
3.马的走法和中国象棋是一样的,日字形,马比较特殊的是,会出现平局的情况,即谁都不能到达(n,m),马的递推是,如果后继中有必败态,那么当前节点是必胜态,如果后继中都是必胜态,那么当前节点是必败态,如果后继中有平局,那么当前节点即为平局。(以上推导严格有序,即在排除了前一种情况的前提下成立)。
4.皇后的走法和王类似,不过王后是走任意步,或者沿斜线任意步。这其实可以看成,两堆石子,要么从一堆取任意个,要么从两堆同时取相同任意个。这就是威佐夫博弈,可见这篇博客。
这题综合考察了几种博弈,还是很不错的,没有接触过博弈的人,可以学到很多。
代码:
#include
#include
#include
#include
#include
#include
#define LL long long
using namespace std;
bool king[1005][1005];
int horse[1005][1005];
bool vis[1005][1005];
int main()
{
int t,type,n,m,s1,s2;
memset(king,0,sizeof(king));
king[0][0]=0;
for(int i=0;i<=1000;i++)
{
for(int j=0;j<=1000;j++)
{
if(king[i][j]==0)
{
if(i+1<=1000)
king[i+1][j]=1;
if(j+1<=1000)
king[i][j+1]=1;
if(i+1<=1000&&j+1<=1000)
king[i+1][j+1]=1;
}
}
}
memset(horse,-1,sizeof(horse));
horse[0][0]=0;
for(int i=0;i<=1000;i++)
{
for(int j=0;j<=1000;j++)
{
s1=-2;s2=-2;
if(i-1>=0&&j-2>=0)
s1=horse[i-1][j-2];
if(i-2>=0&&j-1>=0)
s2=horse[i-2][j-1];
if(s1!=-2&&s2!=-2)
{
if(s1==0||s2==0)
horse[i][j]=1;
else if(s1==-1||s2==-1)
horse[i][j]=-1;
else
horse[i][j]=0;
}
if(s1!=-2||s2!=-2)
{
if(s1==0||s2==0)
horse[i][j]=1;
else if(s1==-1||s2==-1)
horse[i][j]=-1;
else
horse[i][j]=0;
}
}
}
scanf("%d",&t);
while(t--)
{
int flag;
scanf("%d%d%d",&type,&n,&m);
if(type==1)
{
if(king[n-1][m-1]==1)
flag=1;
else
flag=0;
}
else if(type==2)
{
if(n==m)
flag=0;
else
flag=1;
}
else if(type==3)
{
n--;
m--;
if(horse[n][m]==-1)
flag=2;
else if(horse[n][m])
flag=1;
else
flag=0;
}
else
{
int dif,tmp;
n--;
m--;
if(n>m)
swap(n,m);
dif=m-n;
tmp=dif*(1.0+sqrt(5.0))/2;
if(n==tmp)
flag=0;
else
flag=1;
}
if(flag==2)
printf("D\n");
else if(flag==1)
printf("B\n");
else
printf("G\n");
}
return 0;
}