【香蕉OI】游戏(SG函数)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/xyyxyyx/article/details/102755925

文章目录

题意

初始只有一张 n m ( 2 n , m 200 ) n*m(2\le n,m\le 200) 的纸片,每次可以选一张已有的纸片横切一刀(变成 n i n*i 的一片加上 n ( m i ) n*(m-i) 的一片)或者竖切一刀(变成 i m i*m 的一片和 ( n i ) m (n-i)*m 的一片)得到两张纸片。

最先得到 1 1 1*1 的纸片的人获胜。

多组询问,给定初始纸片大小,求先手是否有必胜策略。

思路

简单博弈论。 但是考场上没有想出来。

发现这题与一般SG函数能处理的问题不同的点在于:他只要有一张 1 1 1*1 就算获胜,也就是只要一个子游戏无法进行就算结束;而不是所有纸片都变成 1 1 1*1 才结束,即要求把所有游戏都无法进行才算结束。

但是假如我们把 2 2 2*2 2 3 2*3 3 3 3*3 的纸片看成初始的必败态,情况就不一样了。首先我们排除 1 n ( n > 1 ) 1*n(n > 1) 的纸片,因为切出这种纸片必然导致自己的失败。然后发现对于一个上述三种初始必败的纸片,只要有人先对他下手了,那后手必胜,而游戏结束的条件就不是 “先得到一张 2 2 2*2 2 3 2*3 3 3 3*3 的人赢”,而是 “将所有纸片都变成上述三种纸片的人赢” 。这就是一个正常的SG函数了。

代码

#include<bits/stdc++.h>
using namespace std;
const int N = 200 + 10;
int n = 200, sg[N][N], x, y;
bool tmp[N*2];

void init(int x, int y)
{
	memset(tmp, 0, sizeof(tmp));
	for (int i = 2; i <= x-2; ++ i)
		tmp[sg[i][y]^sg[x-i][y]] = 1;
	for (int i = 2; i <= y-2; ++ i)
		tmp[sg[x][i]^sg[x][y-i]] = 1;
	for (int i = 0; i < (N<<1); ++ i)
		if (!tmp[i]){
			sg[x][y] = i;
			break;
		}
}

int main()
{
	for (int i = 2; i <= n; ++ i)
		for (int j = 2; j <= n; ++ j)
			init(i, j);
	while(scanf("%d%d", &x, &y) == 2)
		puts(sg[x][y] ? "WIN" : "LOSE");
	return 0;
}

猜你喜欢

转载自blog.csdn.net/xyyxyyx/article/details/102755925