POJ Cutting Game(Nim博弈+grundy数)

Description
Urej loves to play various types of dull games. He usually asks other people to play with him. He says that playing those games can show his extraordinary wit. Recently Urej takes a great interest in a new game, and Erif Nezorf becomes the victim. To get away from suffering playing such a dull game, Erif Nezorf requests your help. The game uses a rectangular paper that consists of W*H grids. Two players cut the paper into two pieces of rectangular sections in turn. In each turn the player can cut either horizontally or vertically, keeping every grids unbroken. After N turns the paper will be broken into N+1 pieces, and in the later turn the players can choose any piece to cut. If one player cuts out a piece of paper with a single grid, he wins the game. If these two people are both quite clear, you should write a problem to tell whether the one who cut first can win or not.
Input
The input contains multiple test cases. Each test case contains only two integers W and H (2 <= W, H <= 200) in one line, which are the width and height of the original paper.
Output
For each test case, only one line should be printed. If the one who cut first can win the game, print "WIN", otherwise, print "LOSE".
Sample Input
2 2
3 2
4 2
Sample Output
LOSE
LOSE

WIN


大致题意:题目的大概意思是这样的,就是说给你一个网格纸,大小w*h,让你剪,一人剪一次;随着一次次的剪,纸的数量越来越多,但大小越来越小,谁先剪出1*1的纸张谁就赢。这边其实是Nim博弈的变形,是的你没有看错,就是尼姆博弈的变形,不过就是变得有点大了,变得面目全非了。

何为Nim博弈,大牛见笑了,说给刚接触算法的同学听,所谓的尼姆博弈就是给你n堆石子,数量为a1, a2, a3,...an你从任意一堆里选择任意个,谁没得取谁就输。这一个是经典的题了,直接给结论,当满足a1^a2^a3^..an==0你就输了。就是每一堆的石子数的总亦或值为0的话就是输了。

回到问题上来,如何跟Nim博弈等价起来呢?这边起先只有一张纸,就是相当于只有一堆石子。这边引入一个grundy数的概念,我们现在只以一堆石子为例,其实这边可以说是状态的转移,一堆x个石子,一次可以转化为x-1,x-2, x-3..3, 2, 1.所以grundy值在这边就隐含的表示一张纸对应的石子数,一张纸可以经过剪变成两张纸,这两张纸也对应的各自的grundy数,这是就是一堆石子现在居然变成两堆了。但目前这个状态其实只要取这两个数亦或值又变成一堆。相当于Nim博弈两堆石子亦或可以只当成一堆。

接下来是关键怎么计算grundy数。一个状态的grundy数其实就是他能转移到的状态的grundy值除外的最小非负整数。为什么是这样的呢?首先Nim堆中石子x可以转化为x-1, x-2, x-3, ..2, 1.设当前的状态grundy数为y,那么他一定样要可以转化成y-1, y-2, ...3, 2, 1。举几个例子,比如当前状态可以转化成grundy数为1, 2, 3的状态,那么当前状态grundy数一定是4,这样才能转化为最小的。如果为3 ,5那么当前只能是0。就是说白了就是一个数,比这个数小的数他都能通过变化得到。可能有的人会有疑问,比如可以转化为3, 5的grundy值只能为0,但这个0其实可以变成3或5,这不就说明石子数量会增多吗?其实不然,这个时候先手将grundy值变大了,后手一定可以恢复原来的值,为什么呢?因为每一个grundy数根据他的求法可得一定是可以变为比他小的任意数,这不就说明石子数从变多又可以恢复原样了。

把输入的w*h只当做一堆石子来处理对应的grundy值为0时,就意味着输了。


AC代码:


# include <cstdio>
# include <cstring>
# include <set>
using namespace std;
int grundy[210][210];
int main(){
	int w, h, i, j, k, temp;
	for(i=2; i<=200; i++){
		for(j=2; j<=200; j++){
			int vis[1005];
			memset(vis, 0, sizeof(vis));
			//当出现w==1或h==1是就以为你输了就没得转移了
			for(k=2; i-k>=2; k++){//枚举可以转移到的转态 
				vis[grundy[k][j]^grundy[i-k][j]]=1;
			}
			for(k=2; j-k>=2; k++){//枚举可以转移到的转态 
			    vis[grundy[i][k]^grundy[i][j-k]]=1;
			}
			grundy[i][j]=0;
			for(k=0; ;k++)  
        	if(vis[k]==0){//计算对应的grundy数 
        		grundy[i][j]=k;
        		break;
			}
		}
	}
	while(scanf("%d%d", &w, &h)!=EOF){
		if(grundy[w][h])
		printf("WIN\n");
		else
		printf("LOSE\n");
	}
	return 0;
}



你可能感兴趣的:(ACM,poj,博弈论,Nim,grundy数)