POJ - 2068 Nim (博弈/dp)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/a54665sdgf/article/details/81625059

题目大意:有一堆石子,有2n个人围成一圈轮流取石子,每个人都有自己取石子的最大数目的限制,取到最后一个石子的输。

这2n个人可以分为两支队伍,每支n人,其中第1 3 5...个人是你的队伍,问你的队伍是否有必胜策略。

解法:这道题给的数据量不大,总状态数不多,可以考虑用dp的方法解决。

设dp(s,p)代表当前石子数为s,轮到第p个人取的时候,这个人所面临的状态是必胜态还是必败态。则可以枚举这个人能取的所有石子的数量i,则状态转移到dp(s-i,(p+1)%n),如果至少有一个i满足dp(s-i,(p+1)%n)是必败态,则dp(s,p)为必胜态,反之则为必败态,以此类推。

递归边界为dp(1,p)为必败态。

我还是习惯用记忆化搜索的方法,代码容易写而且不容易出错。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define FRER() freopen("i.txt","r",stdin)
#define FREW() freopen("o.txt","w",stdout)

using namespace std;
typedef long long LL;
int M[25],n,S,d[(1<<13)+100][25];

int dp(int s,int p)
{
    if(~d[s][p])return d[s][p];
    for(int i=1; i<=M[p]; ++i)
    {
        if(s-i<=0)break;
        if(!dp(s-i,(p+1)%n))return d[s][p]=1;
    }
    return d[s][p]=0;
}


int main()
{
    //FRER();
    while(scanf("%d%d",&n,&S)==2)
    {
        n<<=1;
        for(int i=0; i<n; ++i)
            scanf("%d",&M[i]);
        memset(d,-1,sizeof d);
        printf("%d\n",dp(S,0));
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/a54665sdgf/article/details/81625059