Topcoder SRM 701 Div2 900 ThueMorseGame

5 篇文章 0 订阅
1 篇文章 0 订阅

Topcoder SRM 701 Div2 900 ThueMorseGame

Problem Statement

Alice and Bob play a game with a pile of stones. Initially, there are n stones in the pile. The players take alternating turns, Alice goes first. Each turn of the game looks as follows:

The current player choses a number X between 1 and m, inclusive. X cannot exceed the current number of stones in the pile.

The current player removes X stones from the pile.

If the pile is now empty, the current player wins the game.

The current player counts the remaining stones in the pile, and writes that count in binary. If that number contains an odd number of ones, the current player loses the game.

For example, the number 19 written in binary is 10011. This number has three ones in binary, and three is an odd number. Hence, whenever a player produces a pile with exactly 19 stones, they lose the game immediately. You are given the ints n and m. Assume that both Alice and Bob play the game optimally. Return “Alice” if Alice wins, or “Bob” if Bob wins. In other words, return “Alice” if and only if the first player has a winning strategy for the given n and m.

Constraints

n will be between 1 and 500,000,000, inclusive.

m will be between 1 and 50, inclusive.

n will have an even number of ones in binary.

Examples

0)

3

3

Returns: “Alice”

Here Alice can take three stones from the heap and win.

1)

3

2

Returns: “Bob”

If Alice can take one or two stones. Either case, remaining number of stones will contain an odd number of ones in binary.

2)

387

22

Returns: “Alice”

3)

499999999

50

Returns: “Alice”

4)

499999975

49

Returns: “Bob”

Task:

Alice和Bob在玩游戏。

现在有一个数是n,每次操作者可以将此数减小 [1,m] 的数,若减成了0,则当前操作者胜。若减后的数在二进制的表示中1的个数是奇数个,则当前者输。Alice先手,求必胜者。给定的n在二进制的表示中1的个数不会是奇数个。

(n<=500,000,000,m<=50)

Solution:

首先这是个博弈的题,基本的博弈知识就不再阐述了。

首先若n<=m则必定是必胜。然后对于一个二进制表示是奇数个1的数是必胜态,因为当这个状态转移到这个人的身上时,对方已经输了。

于是我们可以简单地通过 O(nm) 的暴力dp完成这个状态的转移。若 [im,i1] 有必败态,则 i 是必胜态,否则是必败态。

但是通过很简单的优化就可以O(n)完成。我们只需要记录最后一个必败态,就可以 O(1) 判断当前点的状态了。

接下来我们就只需要考虑最后一个问题了:如何快速地求一个数的二进制位1的个数。系统其实自带了一个函数__builtin_popcount(),就是用来实现这个功能的,效率也近似 O(1) 。于是我们的代码写起来就类似这样:

class ThueMorseGame {
public:
    string get(int n, int m) {
        if(n<=m)return "Alice";
        int pos=-1,now;
        for(int i=m+1;i<=n;i++){
            if(__builtin_popcount(i)&1||i-pos<=m)now=1;//Win
            else{
                now=0;//Lose
                pos=i;
            }
        }
        return now?"Alice":"Bob";
    }
};

然而坑爹的TC实力卡掉了这个 O(n) 的做法,于是我们自己来实现这个函数,就可以A了233…

int cnt[1<<16],Base=(1<<16)-1;
int Bits(int x){
    int rs=0;
    while(x){
        x-=x&(-x);
        rs++;
    }
    return rs;
}
inline int Popcount(int x){
    return cnt[x>>16]+cnt[x&Base];
}
class ThueMorseGame {
public:
    string get(int n, int m) {
        if(n<=m)return "Alice";
        int pos=-1,now;
        for(int i=1;i<=(1<<16);i++)cnt[i]=Bits(i);
        for(int i=m+1;i<=n;i++){
            if(Popcount(i)&1||i-pos<=m)now=1;//Win
            else{
                now=0;//Lose
                pos=i;
            }
        }
        return now?"Alice":"Bob";
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值