【博弈论】ThueMorseGame

Alice and Bob用一堆石子玩游戏。初始时这堆石子有n颗,玩家轮流操作,Alice先手。
每一次操作为:当前玩家选一个数X(1<=X<=m,X不超过当前这堆石子的总数量),并从该堆取走X颗石子。如果这堆石子被取光,则当前玩家赢。如果这堆石子还有剩余,则把剩余石子数量写成二进制,如果该二进制数含有奇数个1,则当前玩家立刻输掉游戏。
例如,19写成二进制为10011,含有3个1。因此,当某个玩家取走石子后剩余19颗石子,则立刻输掉比赛。
给定n和m,假定两人都按最优策略操作,求谁有必胜策略。

输入格式:
第1行:1个整数n
第2行:1个整数m

输出格式:
第1行:Alice或者Bob

输入样例1:
3
2

输出样例1:
Bob

输入样例2:
387
22

输出样例2:
Alice

数据规模与约定:
1 <= n <= 500,000,000, 且含有偶数个1
1 <= m <= 50

注:本题的时间复杂度与实际耗时相当不科学。因此首先提出最优时间复杂度O(n/m)


题解:
看到这个时间复杂度估计人人都会了,毕竟是很直观很暴力的算法。考虑用N,P来表示结点(N:先手必胜,P:先手必败)
特殊情况:0-P结点,表示前一个人已经拿完,即当前先手已经失败
每一个在二进制下奇数个1的数(简称结束数)-N结点,表示前一个人拿完后,还剩下结束数个石头,即之前的人失败,当前先手必胜

从剩余0个石头开始向上递推,记录目前最大的P结点为cur。不难发现,在每一个P结点向上m个数中,每一个都一定是N结点(都可以一步到达当前这个P结点),所以在没有特殊情况下,下一个P结点一定是cur+m+1。

特殊情况即为cur+m+1刚好是一个结束数,必然为N结点,因此再向上依次+1,直到找到一个不是结束数的数为止。

另外,如果m=1,为避免超时,可以用特别处理来解决,依次从n向下找,直到访问到第一个结束数,通过经过的数字个数,来判断胜负。

这里还要特别介绍一个函数: __builtin_popcount(x)
x是一个整型变量,返回值为x在二进制下1的个数。不需要头文件,是从yl学的黑科技,不推荐大赛使用。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define SF scanf
#define PF printf
using namespace std;
int n,m,cur;
bool flag;
inline bool check(int x){
    return __builtin_popcount(x)%2;
}
int main(){
    SF("%d%d",&n,&m);
    cur=0;
    if(m==1){
        while(!check(n)){
            n--;
            flag=!flag;
        }
        if(!flag)
            PF("Alice");
        else
            PF("Bob");
        return 0;
    }
    while(cur<n){
        cur=cur+m+1;
        while(check(cur))
            cur++;
    }
    if(cur==n)
        PF("Bob");
    else
        PF("Alice");
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值