看了几天博弈论,今天突然豁然开朗,充分理解了SG函数,网上有讲得很好的博弈,三种基本博弈在看完后应该会理解,但是对于理解SG函数,这个链接讲的很
不错,(http://www.cnitblog.com/weiweibbs/articles/42735.html),看第一遍的时候,没人可以看的很明白,所以一定要自己在纸上根据其定义算出几个数的sg值,
例如可选步数集合为{1, 2, 3},算出0到8的sg值,结果如下:
x 0 1 2 3 4 5 6 7 8
g(x) 0 1 2 3 0 1 2 3 0
然后根据计算结果,将上面的那个博客链接里的这段话(首先,所有的terminal position所对应的顶点,也就是没有出边的顶点,其SG值为0,因为它的后继集合是
空集。然后对于一个g(x)=0的顶点x,它的所有后继y都满足g(y)!=0。对于一个g(x)!=0的顶点,必定存在一个后继y满足g(y)=0)挨个字的看,看完这段话就会恍然大悟,知
道sg函数到底是干什么的。然后我想说,上面那个计算的例子其实就是巴什博奕用SG函数来做,他的SG值就是n%(k+1);
理解了SG函数后,对于组合博弈就可以解决了,但一定要记住那句话(游戏的和的SG函数值就是它的所有子游戏的SG函数值的异或),这句话很重要,也是SG的
精髓,知道这个后就可以解决下面两道关于SG函数的题了;
poj 2311 链接 http://acm.hust.edu.cn/vjudge/problem/17242;
题目大意就是说给定一个矩形纸片,然后每个人轮流剪,谁最后剪到1*1就胜利,显然最终状态为(2,2)(2,3)(3,2)时为必输态,所以根据矩形长宽计算其SG值,
然后判断状态就可以了。代码如下:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
int sg[205][205];
int vis[205];
int Sg(int n, int m)
{
if(sg[n][m] != -1)
return sg[n][m];
memset(vis, 0, sizeof(vis));
for(int i=2; i<=n-i; ++i)
{
vis[(Sg(i, m) ^ Sg(n-i, m))] = 1;
}
for(int i=2; i<=m-i; ++i)
{
vis[(Sg(n, i) ^ Sg(n, m-i))] = 1;
}
for(int i=0; i<205; ++i)
if(!vis[i])
{
sg[n][m] = i;
return i;
}
}
int main()
{
int w, h;
memset(sg, -1, sizeof(sg));
sg[2][2] = sg[2][3] = sg[3][2] = 0;
while(scanf("%d%d", &w, &h)!=EOF)
{
if(Sg(w, h))
printf("WIN\n");
else
printf("LOSE\n");
}
return 0;
}
还有一道类似的题目,hdu 5795 链接:
题目大意,两个人玩改进版的尼姆博弈游戏,在原先的基础上,某个人可以将一堆石子分为非空的三堆,然后判断输赢,这道题可以考虑在一堆的情况下的SG值,打表后
会发现规律,打表代码如下:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
int sg[110];
bool vis[1100];
int Sg(int x)
{
memset(vis, 0, sizeof(vis));
for(int i=0; i<x; i++)
{
vis[sg[i]] = true;
}
int ans;
for(int i=1; i<x; i++)
{
for(int j=1; j+i<x; j++)
{
vis[sg[i]^sg[j]^sg[x-i-j]] = true;
}
}
for(int i=0; i<=1000; i++)
if(!vis[i])
return i;
}
int main()
{
memset(sg,-1,sizeof(sg));
sg[0] = 0;
for(int i=1; i<=100; i++)
{
sg[i] = Sg(i);
}
for(int i=0; i<=100; i++)
{
printf("sg[%d] = %d\n", i, sg[i]);
}
return 0;
}
ac代码如下:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
int main()
{
int T, n, t;
scanf("%d", &T);
while(T--)
{
scanf("%d", &n);
int sum = 0;
for(int i=1; i<=n; ++i)
{
scanf("%d", &t);
if(t%8 == 0) t--;
else if(t%8 == 7) t++;
sum ^= t;
}
if(sum)
printf("First player wins.\n");
else
printf("Second player wins.\n");
}
return 0;
}