poj2311(grundy博弈论)

题目意思就是说两个人轮流剪纸片,直到有一个人剪出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;
}

猜你喜欢

转载自blog.csdn.net/qq_39861441/article/details/82927390