题目链接:
http://poj.org/problem?id=2311
题意:
给定一张纸,由 w×h(2≤w,h≤200) 个方格组成,两个人轮流横着或者竖着剪一刀,接下来在剪完的所有纸中选择一张继续剪,问谁最先剪出 1×1 的方格。
分析:
假设
(w,h)
的纸剪一刀之后变成
(w1,h1),(w2,h2)
,可以将当前看成两个子游戏的并,即
sg[w][h]=sg[w1][h1] xor sg[w2][h2]
。
最初把
(1,1)
设为游戏终止状态, 结果都调不出样例。仔细一想是不对的,因为题目中只要有一个子游戏出现
(1,1)
即为输,而多个组合游戏的并要求的是走完之后所有游戏都到达终止状态,也就是说只有全部子游戏都达到
(1,1)
游戏才结束,明显与题意不符。
我们可以转换一下, 在
(1,1)
出现之前必然不会有
(1,n),(n,1)
出现(这个现象肯定是双方都避免创造给对手的),继续推,发现
(2,2),(2,3),(3,2),(3,3)
为不可再分的状态,无需再切即可获得输赢,切出
(1,1)
的过程中必经过这些状态中的某些。
也就是说只有所有纸为
(2,2)、(2,3)、(3,2)、(3,3)
时能保证再切两次切出
(1,1)
,为必败态,满足组合游戏并的要求,可以作为终态,当所有子游戏都达到这个状态时,游戏结束。
代码:
/*************************************************************************
> File Name: 2311.cpp
> Author: jiangyuzhu
> Mail: 834138558@qq.com
> Created Time: 2016/7/30 14:53:50
************************************************************************/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<algorithm>
using namespace std;
const int maxn = 2e2 + 5;
int sg[maxn][maxn];
bool vis[maxn];
int gao(int x, int y)
{
memset(vis, false, sizeof(vis));
for(int i = 2; i < x - 1; i++){
int tmp = sg[i][y] ^ sg[x - i][y];
vis[tmp] = true;
}
for(int i = 2; i < y - 1; i++){
int tmp = sg[x][i] ^ sg[x][y - i];
vis[tmp] = true;
}
for(int i = 0;; i++){
if(!vis[i]) return sg[x][y] = i;
}
}
int main (void)
{
int n, k;
for(int i = 2; i < maxn; i++){
for(int j = 2; j < maxn; j++){
if(i == 2 && j == 2) sg[i][j] = 0;
else if(i == 2 && j == 3) sg[i][j] = 0;
else if(i == 3 && j == 2) sg[i][j] = 0;
else if(i == 3 && j == 3) sg[i][j] = 0;
else sg[i][j] = gao(i, j);
}
}
while(~scanf("%d%d", &n, &k)){
if(sg[n][k]) puts("WIN");
else puts("LOSE");
}
return 0;
}