<p>
</p><p>#include<cstdio></p>#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<iostream>
using namespace std;
typedef long long LL;
int sg[1<<21];
bool h[25];
void init_SG()
{
memset(sg,0,sizeof sg);
for(int i=(1<<20)-1;i>=0;i--)
{
memset(h,0,sizeof h);
for(int j=0;j<20;j++)
{
if((i&(1<<j))==0) continue;
for(int k=j+1;k<20;k++)
{
if((i&(1<<k))!=0) continue;
h[sg[i-(1<<j)+(1<<k)]]=1; break;
}
}
for(int j=0;j<=20;j++) if(h[j]==0) { sg[i]=j; break; }
}
}
int main()
{
init_SG();
int T; scanf("%d",&T);
while(T--)
{
int n; scanf("%d",&n);
int ans=0;
for(int i=1;i<=n;i++)
{
int k,y=0; scanf("%d",&k);
while(k--)
{
int f; scanf("%d",&f); f--;
y=y|(1<<f);
}
ans=ans^sg[y];
}
if(ans) printf("YES\n");
else printf("NO\n");
}
return 0;
}
因为一行最多只有20个数,也就是说只有(1<<20)种状态,向右移动表示小的数推向了大的数。可以用SG函数预处理出所有情况。然后把每一行的SG函数值异或一下,非零则必胜,否则输。
组合博弈的通解就是sg函数,学习了sg函数之后一直没有咋用过。
学习博弈的可以在nyoj上面做10道取石子题目,作为了对博弈也就有一定理解了。
用的时候注意初始的时候只要初始sg[0]=0;
其他都通过函数求解。
这里贴一个求解sg函数的模板。
用set容器实现的方法,原理一样。oj上容易超时
hdoj 1536 和pku 2960 S-Nim
题意就是给出一个数组s。为每次可以取石子的数目。
然后给你n堆石子每堆si。求解先手能不能赢!标准的sg函数用法题目。
代码: