HIT暑期集训 博弈

学长的博弈总结博客 orz

博弈SG函数模板

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define maxn 1005 
using namespace std;
int f[maxn],sg[maxn],mex[maxn];
void get_f(int n)
{
    int i;
    f[1]=1;f[2]=2;
    for (i=3;f[i-1]<n;i++) f[i]=f[i-1]+f[i-2];
}
void get_sg(int n)
{
    int i,j;
    memset(sg,0,sizeof(sg));
    for (i=1;i<=n;i++)
    {
        memset(mex,0,sizeof(mex));
        for (j=1;f[j]<=i;j++)
            mex[sg[i-f[j]]]=1;
        for (j=0;j<=n;j++)
            if (!mex[j]) break;
        sg[i]=j;
    }
}
int main()
{
    int m,n,p;
    get_f(1000);
    get_sg(1000);
    while (scanf("%d%d%d",&m,&n,&p)!=EOF && m!=0)
    {
        if (sg[m]^sg[n]^sg[p]) printf("Fibo\n");
        else printf("Nacci\n");
    }
    return 0;
}
博弈SG模板,HDU1848

C    HDU 1404

因为输入的字符串只有6位,就可以把它当作一个6位整数x。

从(先手)必败状态x开始,可进行的操作有①在x的某一位上加上一个数(加上后该位不超过9)②当x不足6位,可以在x后面加0(即乘以10或100...)

这样由必败状态推出的所有状态均为必胜状态。

预处理1000000以内的所有数的胜败状态,每次输入时根据对应值输出即可。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define maxn 1000005 
using namespace std;
int sg[maxn];
char s[12];
void get_sg(int x)
{
    int i,j,y=x,l=0,now,k;
    while (y)
    {
        l++;y=y/10;
    }
    for (i=1,k=1;i<=l;i++,k=k*10)
    {
        now=x/k%10;
        y=x;
        for (j=now;j<9;j++)
        {
            y+=k;
            sg[y]=1;
        }
    }
    y=x;
    for (k=1;l<6;l++,k=k*10)
    {
        y=y*10;
        for (i=0;i<k;i++) sg[y+i]=1;
    }
}
int main()
{
    int i,n,m,len,x;
    sg[0]=1;
    for (i=1;i<1000000;i++)
        if (!sg[i]) get_sg(i);
    while (scanf("%s",s)!=EOF)
    {
        if (s[0]=='0') printf("Yes\n");
        else 
        {
            x=0;
            len=strlen(s);
            for (i=0;i<len;i++) x=x*10+s[i]-'0';
            if (sg[x]) printf("Yes\n");
            else printf("No\n");
        }    
    }
    return 0;
}
View Code

E    HDU 3980

第一个人先涂m个,把n长的链变为n-m长的环,每次的结果等于涂色后分出的两端的答案的异或和。据此就可递归求出sg了。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define maxn 1005 
using namespace std;
int sg[maxn],mex[maxn],n,m;
int findsg(int x)
{
    if (sg[x]!=-1) return sg[x];
    if (x<m) return sg[x]=0;
    int i;
    memset(mex,0,sizeof(mex));
    for (i=m;i<=x;i++)
        mex[findsg(i-m)^findsg(x-i)]=1;
    for (i=0;;i++)
        if (!mex[i]) break;
    return sg[x]=i;
}
int main()
{
    int T,t,i;
    scanf("%d",&T);
    for (t=1;t<=T;t++)
    {
        scanf("%d%d",&n,&m);
        printf("Case #%d: ",t);
        if (n<m) printf("abcdxyzk\n");
        else 
        {
            n=n-m;
            memset(sg,-1,sizeof(sg));
            for (i=0;i<=n;i++) sg[i]=findsg(i);
            if (sg[n]) printf("abcdxyzk\n");
            else printf("aekdycoin\n");
        }
    }
    return 0;
}
View Code

F    CodeForces 1147C

设这n堆石子中,石子数量最少为x,cnt数组记录不同石子数量在n堆石子中出现的次数。

则若cnt[x]<=n/2,此时必胜;若cnt[x]>n/2,此时必败,因为cnt[x]<=n/2可以通过取石子的方式转移至该状态。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define maxn 55 
#define inf 99999999
using namespace std;
int cnt[maxn];
int main()
{
    int i,x,n,re=inf;
    scanf("%d",&n);
    for (i=1;i<=n;i++) 
    {
        scanf("%d",&x);
        cnt[x]++;
        re=min(re,x);
    }
    if (cnt[re]<=n/2) printf("Alice\n");
    else printf("Bob");
    return 0;
}
View Code

G    CodeForces 936B

H    POJ 1704

I    POJ 2425

题意:给一个图,图中有m条有向边,给出几个棋子的初始位置,游戏双方轮流沿着边移动这几个棋子,最后无法移动这几个棋子的人输。

思路:把每个棋子看作一个子游戏,通过记忆化搜索求出sg数组后,将每个棋子的答案异或就是最终答案。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define maxn 1005 
using namespace std;
int sg[maxn],mp[maxn][maxn],n,ans;
int dfs(int x)
{
    if (sg[x]!=-1) return sg[x];
    int mex[maxn];
    memset(mex,0,sizeof(mex));
    int i;
    for (i=0;i<n;i++)
        if (mp[x][i]) mex[dfs(i)]=1;
    for (i=0;;i++) 
        if (!mex[i]) break;
    return sg[x]=i;
}
int main()
{
    int m,x,i;
    while (scanf("%d",&n)!=EOF)
    {
        memset(mp,0,sizeof(mp));
        memset(sg,-1,sizeof(sg));
        for (i=0;i<n;i++)
        {
            scanf("%d",&m);
            if (m==0) sg[i]=0;
            while (m--)
            {
                scanf("%d",&x);
                mp[i][x]=1;
            }
        }
        for (i=0;i<n;i++)
        { 
            sg[i]=dfs(i);
        }
        while (scanf("%d",&m)!=EOF && m!=0)
        {
            ans=0;
            while (m--)
            {
                scanf("%d",&x);
                ans=ans^sg[x];
            }
            if (ans) printf("WIN\n");
            else printf("LOSE\n");
        }
    }
    return 0;
}
View Code

 (补充一个网上找到讲博弈论基础的博客https://www.cnblogs.com/lfri/p/10662291.html

posted @ 2020-09-01 10:11  lsy_kk  阅读(198)  评论(0编辑  收藏  举报