hdu2486 A simple stone game K倍动态减法游戏

     巨坑的一道博弈...思路太奇葩了...理解能力强的推荐去看2009年国家集训队论文  曹钦翔《从“k倍动态减法游戏”出发探究一类组合游戏问题》里面给有详细的证明过程...

先说一下题意,N个石子,第一次可以去1---N-1中任意数量的,之后每次最少取1个,最多取k*上次去的石子数,取走最后一颗的胜利,网上题解也不少,我是感觉都不怎么好懂, 我做的时候参考了这里的文章:http://blog.csdn.net/acm_cxlove/article/details/7836544。下面说说我自己的理解吧。

     首先讨论K=1的情况,必败态为所有形同2^i的数,因为如果不是这样的数的话,拆成二进制表示一定会含有两个以上的1,那么先手只要把最低位的1去掉,后手无法取掉任何高位的1,那么取完的结果一定会产生新的1,而我们每次保持取走最低位的1的操作,直到最后就可以把最后一个1取走。

     K=2的时候,必败态构成斐波那契数列,这里用到一个斐波那契数列的性质,即任何数都可以表示成若干个“互不相邻的”斐波那契数的和,而不相邻的斐波那契数所差的倍数都是大于2的,那么我们就可以类比K=1的情况,把N按这种“斐波那契数列”的数制分解,每次仍然是取走最低位的1,由于后手无法取走高两位之上的1而前边的不相邻有保证了不会有连续的1出现,所以接下来就和K=1的时候一样了,每次取走最低位的1知道结束。

     K>2的时候,我们就需要构造出一个数列A[],使得N可以有这个数列中若干项相加而成并且选取的项互相直接的倍数严格大于K,够早的方式类似数学归纳法。假设现在A[1...i]已经构造完成,引入新的数组B[]满足B[I]为1...A[I]可以组成的最大的数,注意这里的B[I]一定要用到A[I],否则就是又A[i-1]构成的了。接下来构造A[i+1],因为B[i]已经是之前的A[]构造出来的最大值了,那么B[i]+1是无法被构造的,所以有A[i+1]=B[i]+1.而根据定义b[i+1]是由1--A[i+1]构造出来的最大的数,并且A[i+1]一定要用到,所以这里要找到一个A[j]*k<A[i],所以有B[i+1]=A[i+1]+B[j]。

    构造完成后如果N在数列A[]中那么显然必败,否则就从大到下遍历A[i]每次在N中减掉A[i]直到N==0,最后一个减掉的数就是第一步最小的取值方案。

#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
using namespace std;
typedef long long ll;
int a[2020000],b[2020000];
int n,m,k;
int main()
{
    int tt,sp=0;
    scanf("%d",&tt);
    while(tt--)
    {
        scanf("%d%d",&n,&k);
        a[0]=b[0]=1;
        int i=0,j=0;
        while(a[i]<n)
        {
            i++;
            a[i]=b[i-1]+1;
            while (a[j+1]*k<a[i]) j++;
            if (a[j]*k<a[i]) b[i]=b[j]+a[i];
            else b[i]=a[i];

        }

        printf("Case %d: ",++sp);
        if (a[i]==n) puts("lose");
        else
        {
            int last;
            while (n)
            {
                if (n>=a[i]) n-=a[i],last=a[i];
                i--;
            }
            printf("%d\n",last);
        }

    }

    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值