sgu153 Playing with matches (博弈dp)#by nobody


http://acm.sgu.ru/problem.php?contest=0&problem=153

题意:有N根火柴,m个数p1,p2,p3,..pm(m<=8,pi<=9),每个人可以轮流取1根或者pi根火柴,问先取的还是后取的有必胜策略。



解析:这个题有点类似与取石子游戏,我们不记f[n]为n根火柴时先取的是否有必胜策略(1或0).那么f[i]是只与f[i-1], f[i-p[1]], f[i-p[2]], ... f[i-p[m]]有关的,如果这些状态都是赢,那么f[i]就是输,否则f[i]是赢.

但是n太大了10^9。注意到pi最大只有9,那么pi必定是由i前9之内的某个或某些状态推出来的,由于九个状态的组合只有2^9 = 512 种,如果找到2个相等的话就找到了周期,那么f必存在一个小于512的周期。

所以问题转化为了找f的周期,求值.


code

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

int main()
{
    //freopen("input.txt","r",stdin);
    int k;
    scanf("%d",&k);
    int p[100];
    int f[512];
    int pos[512];
    while(k--)
    {
        memset(pos,0,sizeof(pos));
        int m,n;
        scanf("%d%d",&n,&m);
        for(int i=0;i<m;i++)
            scanf("%d",&p[i]);
        p[m++] = 1;
        sort(p,p+m);
        int t = 0;
        for(int i=0;i<m;i++)
            if(p[i]!=p[i+1] || i == m-1)p[t++] = p[i];
        m = t;
        f[1] = 0;
        int l2 = 2,l1 = -1;
        int x = 0;
        while(l1 == -1)
        {
            f[l2] = 1;
            for(int i=0;i<m && l2-p[i] > 0 && f[l2];i++) f[l2] &= f[l2-p[i]];
            f[l2] = !f[l2];
            if(pos[x]) l1 = pos[x];
            else
            {
                pos[x] = l2;
                x = (x<<1) & 0x1ff | f[l2];
                l2++;
            }
        }
        int ans;
        if(n<=l1) ans = f[n];
        else ans = f[(n-l1)%(l2-l1)+l1];
        if(ans)printf("FIRST PLAYER MUST WIN\n");
        else printf("SECOND PLAYER MUST WIN\n");
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值