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函数的规律

HDU 3032 multi-sg 打表找规律

[hdu-5795]A Simple Nim 博弈 尼姆博弈 SG函数打表找规律

51 nod1067 Bash游戏 V2(sg函数打表)