一堆变多堆!
所谓Multi-SG就是每次操作完了以后一个单一游戏可能分裂成两个单一游戏。实际上这个的处理也非常方便,因为一个状态的后继变成了两个状态,而下一名玩家可以选择两个状态中的任意一个进行操作,所以影响到后继结果的应该是这两个状态构成的一个整体。那么只需要把这两个分别求出来SG值然后“加起来”也就是异或一下然后计入后继就可以了。
1、
POJ2311 Cutting Game
题意:
给出n×m的方格纸片,一次只能剪一刀,最先得到1×1纸片的人获胜。
题解:
对于博弈问题,求SG是比较关键的了
这里的状态是一个二维的,那么就考虑寻找(i,j)的直接后继状态,枚举横着剪还是竖着剪然后递归下去就好啦。
可以发现到2*2的格子是必败状态,sg=0,所以枚举k计算后继的时候从2开始
还有一个比较关键的问题就是对于一个纸片(i,j),把它剪成的两个纸片并不是独立的,也就是状态(i,j)的胜败取决于这两个纸片合起来的SG值而不是其中某一个,所以后继状态的SG值应该是这两个纸片SG值的异或值。这一段话也就是Multi-SG的解法
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int sg[205][205],w,h;
void get_sg()
{
bool ext[40010];
for (int x=2;x<=200;x++)
for (int y=2;y<=200;y++)
{
memset(ext,0,sizeof(ext));
for (int i=2;i+i<=x;i++)//再枚举下去就重复啦
ext[sg[i][y]^sg[x-i][y]]=1;
for (int j=2;j+j<=y;j++)
ext[sg[x][j]^sg[x][y-j]]=1;
for (int k=0;;k++)
if (!ext[k]){sg[x][y]=k; break;}
}
}
int main()
{
get_sg();
while (~scanf("%d%d",&w,&h))
if (sg[w][h]==0) printf("LOSE\n");
else printf("WIN\n");
}
2、
[BZOJ2940] [Poi2000] 条纹
题解:
可以类似刚才的题目考虑一下,注意每种决策要分开讨论,因为一种布料不可以放,其他两种可能还能放,Multi-SG的问题分开异或指的是当一块布料把这个棋盘分成两部分时两部分要分开异或
代码:
#include <cstdio>
#include <cstring>
using namespace std;
int sg[1005],c,z,n,m,p;
void get_sg()
{
bool ext[1005];
for (int i=1;i<=1000;i++)
{
memset(ext,0,sizeof(ext));
for (int j=