【lightOJ】 Partitioning Game (博弈,sg函数)


抄来的题解:

http://blog.youkuaiyun.com/jasison/article/details/27372913

1. LightOJ 1199 - Partitioning Game
题意:有n堆石子(1<=n<=100),每一堆分别有xi个石子(1<=xi<=10000),
一次操作可以使一堆石子变成两堆数目不相等的石子,
最后不能操作的算输,问先手胜还是后手胜。
思路:n堆石子相互独立,所以可以应用SG定理,只需要算出一堆石子的SG函数。
一堆石子(假设有x个)的后继状态可以枚举出来,分别是{1,x-1},{2,x-2},...,{(x-1)/2,x-(x-1)/2},
一堆石子分成的两堆石子又相互独立,再次应用SG定理。所以SG(x) = mex{ SG(1)^SG(x-1), SG(2)^SG(x-2),..., SG((x-1)/2)^SG(x-(x-1)/2) },
最后的答案是SG(x1)^SG(x2)^...^SG(xn)


附上代码(递归和非递归)及其效率比较:




#include <stdio.h>

int sg[11111];

int dfs(int n)
{
    if(sg[n]!=-1)
        return sg[n];
    int res=0,i;
    int vis[200];//vis不能初始化为全局变量,因为每一层dfs都需要一个‘自己’的vis数组
    for(i=0;i<200;++i)
         vis[i]=0;
    for(i=1;i*2<n;++i)
        vis[dfs(i)^dfs(n-i)] = 1;

    i=0;

    while(vis[i])
        i++;

    sg[n]=i;
    return sg[n];
}


int main()
{
    int T,n,cas=0,i,x,sum;

    for(i=0;i<10001;++i)
        sg[i]=-1;
    scanf("%d",&T);

    while(T--)
    {
        scanf("%d",&n);
        sum=0;
        for(i=0;i<n;++i)
        {
            scanf("%d",&x);
            sum^=dfs(x);
        }
        printf("Case %d: ",++cas);
        if(sum)
            puts("Alice");
        else
            puts("Bob");

    }
    return 0;
}


#include <stdio.h>
#define N 11111
int sg[N];
int u[N];
int dfs(int n)
{
    int i,j;
    sg[1]=0;
    sg[2]=0;
    for(i=3;i<n;++i)
    {
        for(j=0;j<n;++j)
            u[j]=0;
        for(j=1;j*2<i;++j)
        {
            u[sg[j]^sg[i-j]]=1;
        }
        int k=0;
        while(u[k])
            k++;
        sg[i]=k;
    }
}

int main()
{
    dfs(10001);

    int T,n,cas=0,i,x,sum;
    scanf("%d",&T);

    while(T--)
    {
        scanf("%d",&n);
        sum=0;
        for(i=0;i<n;++i)
        {
            scanf("%d",&x);
            sum^=sg[x];
        }
        printf("Case %d: ",++cas);
        if(sum)
            puts("Alice");
        else
            puts("Bob");

    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值