SG函数学习
尼姆博弈就是sg函数的简单体现
学习粗:https://blog.csdn.net/luomingjun12315/article/details/45555495
//f[N]:可改变当前状态的方式,N为方式的种类,f[N]要在getSG之前先预处理 //SG[]:0~n的SG函数值 //S[]:为x后继状态的集合 int f[N],SG[MAXN],S[MAXN]; void getSG(int n){ int i,j; memset(SG,0,sizeof(SG)); //因为SG[0]始终等于0,所以i从1开始 for(i = 1; i <= n; i++){ //每一次都要将上一状态 的 后继集合 重置 memset(S,0,sizeof(S)); for(j = 0; f[j] <= i && j <= N; j++) S[SG[i-f[j]]] = 1; //将后继状态的SG函数值进行标记 for(j = 0;; j++) if(!S[j]){ //查询当前后继状态SG值中最小的非零值 SG[i] = j; break; } } }
题:http://acm.hdu.edu.cn/showproblem.php?pid=1536
分析,对石子堆打表出sg函数
异或和要求只能去的数
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> using namespace std; const int N=10005; int s[110],sg[N+4],f[110]; void getsg(int n){ sg[0]=0; for(int i=1;i<N;i++){ memset(s,0,sizeof(s)); for(int j=1;f[j]<=i&&j<=n;j++){ s[sg[i-f[j]]]=1; } for(int j=0;j<N;j++){ if(!s[j]){ sg[i]=j; break; } } } } int main(){ int k; while(scanf("%d",&k)!=EOF){ if(k==0) break; for(int i=1;i<=k;i++) scanf("%d",&f[i]); sort(f+1,f+1+k); getsg(k); int t; scanf("%d",&t); while(t--){ int n; scanf("%d",&n); int ans=0; for(int x,i=1;i<=n;i++){ scanf("%d",&x); ans^=sg[x]; } if(ans) printf("W"); else printf("L"); } printf("\n"); } return 0; }
题:http://acm.hdu.edu.cn/showproblem.php?pid=5724
由状压得来的sg函数。
题意:n*20的棋盘,给你n行,每行m个,每位选手把棋子移到右边第一个空的位置,移不动则输,问先手是否必赢
#include<bits/stdc++.h> using namespace std; typedef long long ll; #define pb push_back const int M=1e3+3; int sg[1<<21]; int sta[30]; void init(){ for(int i=0;i<(1<<20);i++){ memset(sta,0,sizeof(sta)); for(int j=19;j>=0;j--) if(i&(1<<j)){ for(int k=j-1;k>=0;k--){ if(!(i&(1<<k))){ sta[sg[i^(1<<k)^(1<<j)]]=1; break; } } } for(int j=0;j<=19;j++) if(sta[j]==0){ sg[i]=j; break; } } } int main(){ init(); int t; scanf("%d",&t); while(t--){ int n; int ans=0; scanf("%d",&n); for(int i=1;i<=n;i++){ int m; int sign=0; scanf("%d",&m); while(m--){ int x; scanf("%d",&x); sign|=1<<(20-x); } ans^=sg[sign]; } if(ans) puts("YES"); else puts("NO"); } return 0; }