题意:给定一张n*m的矩形纸片,定义一个决策为:从所有的纸片中任选一张(初始时仅有1张),沿平行于矩形边剪切,将原矩形分割为2个较小的矩形,双方轮流决策,先剪出1*1纸片者获胜,问先手是否必胜.
经典的组合游戏问题,首先一张纸片经过一次裁剪变为两张,即一个游戏变为两个子游戏,那么子游戏和的sg值=子游戏A的sg值异或子游戏B的sg值,即sg[A∪B] = sg[A] xor sg[B].
很容易想出将(1,1)作为终止条件.但是考虑组合游戏本质为在一张有向图中从S到T双方轮流移动,但此题中只要出现一张(1,1)游戏便结束,以(1,1)作为终止条件可能会产生有向图并没有走完但游戏已经结束的情况.
稍加分析可知,某一方某一次决策之后产生了一张(1,k)的纸片,那么这一方必败(因为另一方将(1,k)裁剪为(1,1)&(1,k-1)可以直接获胜),那么对于(i,j)的纸片,最优决策一定会避免剪出(1,k)的纸片,当纸片无论怎么剪都会出现(1,k),称这张纸片为"不可剪",当某纸片不可剪时,则会选择其他纸片进行裁剪,易证,存在某一个状态,所有的纸片都是"不可剪".
不可剪状态的纸片有:(2,2), (2,3), (3,2), (3,3).
那么只需要将这些纸片作为终止条件问题就解决了.
code:
<span style="font-size:18px;">#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define rep(i, l, r) for (int i = l; i <= r; i++)
#define REP(i, l, r) for (int i = l; i >= r; i--)
#define MAXN 1010
int n, m, sg[MAXN][MAXN];
bool xx[MAXN];
inline int mex() {int i; for (i = 0; xx[i]; ++i); return i;}
inline int getsg(int n, int m) {
if (~sg[n][m]) return sg[n][m];
memset(xx, 0, sizeof(xx));
rep(i, 2, n-2) xx[getsg(i, m) ^ getsg(n-i, m)] = 1;
rep(i, 2, m-2) xx[getsg(n, i) ^ getsg(n, m-i)] = 1;
return sg[n][m] = mex();
}
int main() {
memset(sg, -1, sizeof(sg));
sg[1][1] = sg[2][2] = sg[3][3] = sg[2][3] = sg[3][2] = 0;
while (scanf("%d%d", &n, &m) != EOF)
cout << (getsg(n, m) ? "WIN" : "LOSE") << endl;
return 0;
}
</span>