sg函数打表
Posted 华东交通ACM训练基地
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了sg函数打表相关的知识,希望对你有一定的参考价值。
打表:
//f[]:可以取走的石子个数 //sg[]:0~n的SG函数值 //hash[]:mex{} int f[N],sg[N],hash[N]; void getSG(int n) { int i,j; memset(sg,0,sizeof(sg)); for(i=1;i<=n;i++) { memset(hash,0,sizeof(hash)); for(j=1;f[j]<=i;j++) hash[sg[i-f[j]]]=1; for(j=0;j<=n;j++) //求mes{}中未出现的最小的非负整数 { if(hash[j]==0) { sg[i]=j; break; } } } }
搜索:
//注意 S数组要按从小到大排序 SG函数要初始化为-1 对于每个集合只需初始化1遍 //n是集合s的大小 S[i]是定义的特殊取法规则的数组 int s[110],sg[10010],n; int SG_dfs(int x) { int i; if(sg[x]!=-1) return sg[x]; bool vis[110]; memset(vis,0,sizeof(vis)); for(i=0;i<n;i++) { if(x>=s[i]) { SG_dfs(x-s[i]); vis[sg[x-s[i]]]=1; } } int e; for(i=0;;i++) if(!vis[i]) { e=i; break; } return sg[x]=e; }
51nod 1070
在知道这是斐波那契博弈之前打表打错了,菜到变形.jpg
由于每次取的规则都有变化,所以要用特殊的记忆化搜索
正确的打表姿势:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<stack> #include<map> #include<vector> #include<queue> #include<set> #include<iomanip> #include<cctype> #include<stack> using namespace std; const int MAXN=1e6+5; const int INF=1<<30; const long long mod=1e9+7; const double eps=1e-8; #define ll long long #define edl putchar(‘\n‘) #define sscc ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); #define FOR(i,a,b) for(int i=a;i<=b;i++) #define ROF(i,a,b) for(int i=a;i>=b;i--) #define FORLL(i,a,b) for(ll i=a;i<=b;i++) #define ROFLL(i,a,b) for(ll i=a;i>=b;i--) #define mst(a) memset(a,0,sizeof(a)) #define mstn(a,n) memset(a,n,sizeof(a)) #define zero(x)(((x)>0?(x):-(x))<eps) int sg[1005][1005]; int dfs(int x,int limit,int flag)//0 win 1lose { //cout<<x<<" "<<limit<<" "<<flag<<endl; if(limit!=-1&&sg[x][limit]!=-1) return sg[x][limit]^!flag; if(limit!=-1&&x<=2*limit) { sg[x][limit]=flag; return flag; } int a[5]={0,0},maxx=2*limit; if(limit==-1) maxx=x-1; FOR(i,1,maxx) { a[dfs(x-i,i,!flag)]++; } if(a[flag]==0) { sg[x][limit]=!flag; return !flag; } else { sg[x][limit]=flag; return flag; } } int main() { FOR(i,1,1000) FOR(j,1,1000) sg[i][j]=-1; FOR(i,2,1000) { if(dfs(i,-1,0)) cout<<i<<endl; } }
顺便考虑了一下,把规则改成可以取三倍的情况(只要把搜索里的2*limit改成3*limit即可),得到了一个这样的数列:
2 3 4 6 8 11 15 21 29 40 55 76 105 145 200 276 381 526 726
以上是关于sg函数打表的主要内容,如果未能解决你的问题,请参考以下文章
博弈论 SG函数Fibonacci again and again
Nowcoder 挑战赛23 B 游戏 ( NIM博弈SG函数打表 )
HDU 5795 A Simple Nim 打表求SG函数的规律