题目意思就是说两个人轮流剪纸片,直到有一个人剪出1*1的方格就算这个人赢了。然后给出纸片的长和宽,求先手会赢还是会输
思路:直接使用grundy即可,但是深层的原理却不是很了解,后面会仔细推导一下
(补充,摘自其他博客):
Grundy值:除(任意一步所能转移到 的状态 的Grundy值 )以外的最小非负整数,这样的Grundy值,和Nim中的一个石子堆类似,有如下性质:
mex{0,1,2}=3;mex{ 1, 2}=0 ; mex{ 2, 3}=1
1.Nim中有x颗石子的石子堆,能转移成有0,1,……,x-1堆石子的石子堆
2.从Grundy值为x的状态出发,可以转移到Grundy值为0,1,……,x-1的状态。
现在来让我简单的谈一下对这道题的理解:
首先p-position(必输)态肯定是0的,所以对sg[2][2],sg[2][3],sg[3][2]先把状态赋值为0.然后用动态规划从这三个必输态开始往前推出各个sg值。flag中记录的是前一个状态值。dfs(w-i,h)^dfs(i,h)表示这两个状态(w-i,h)和(i,h)联合的(w,h)的一个子状态。然后就从所有自状态中用mex()运算,求出现在的(w,h)状态的sg[w][h]值。至于为什么初始值只要设sg[2][2],sg[2][3],sg[3][2]即可,自己想吧,挺简单的。
#include<cstdio>
#include<cstring>
using namespace std;
int sg[201][201];
int dfs(int w,int h)
{
if(sg[w][h]>=0)
return sg[w][h];
int flag[205]={0};
for(int i=2;i<=w/2;i++)
{
flag[dfs(w-i,h)^dfs(i,h)]=1;
}
for(int i=2;i<=h/2;i++)
{
flag[dfs(w,h-i)^dfs(w,i)]=1;
}
for(int i=0;;i++)
if(!flag[i])
{
sg[w][h]=i;
return i;
}
}
int main()
{
//freopen("t.txt","r",stdin);
int w,h;
memset(sg,-1,sizeof(sg));
sg[2][2]=sg[2][3] = sg[3][2] = 0;
while(scanf("%d%d",&w,&h)!=EOF)
{
if(dfs(w,h)) printf("WIN\n");
else printf("LOSE\n");
}
return 0;
}