hdu - 4315 - Climbing the Hill - 博弈

http://acm.hdu.edu.cn/showproblem.php?pid=4315

如图,谁先把红球移出去谁赢。移的方式是不能超越着移。

解:

  Nim:

通常的Nim游戏的定义是这样的:有若干堆石子,每堆石子的数量都是有限的,合法的移动是“选择一堆石子并拿走若干颗(不能不拿)”,如果轮到某个人时所有的石子堆都已经被拿空了,则判负(因为他此刻没有任何合法的移动)。
对于一个Nim游戏的局面(a1,a2,...,an),它是P-position当且仅当a1^a2^...^an=0,其中^表示异或(xor)运算。
为什么可以a1^a2^...^an=0 就判断先取的一定输呢,首先是如果堆内的石子数量成对相等,这个应该都明白,就是2,2的情况或者4,4,9,9,这样,谁先取谁输,但如果是三组的呢,怎么办,用a1^a2^...^an=0,啊!!简洁的说就是将所有堆数全都分解成二进制堆,譬如9,5,12,谁先取谁输,为什么,就是9的二进制1001分成8和1,5的二进制0101分成4和1,12的二进制1100分成8和4,然后9,5,12就变成1,1,4,4,8,8了这不就是成对了么,这就是为什么a1^a2^...^an=0就可一判断谁先走谁输了。


此题的简化版本是不考虑King的存在,双方一直走到不能走的一方为负。此时的解法是根据人数的奇偶性:把人从上顶向下的位置记为a1,a2,...an, 如果为偶数个人,则把a(2i-1)和a(2i)之间的距离当做一个Nim堆,变成一共n/2堆的Nim游戏;如果为奇数个人,则把山顶到a1的距离当做一个Nim堆,a(i*2)到a(i*2+1)的距离当做Nim堆,一共(n+1)/2堆。

考虑King的情况和上述版本几乎一致,只要把King当作普通人一样处理即可。除了两种特殊情况:1. 当King是第一个人时,Alice直接胜 2. 当King是第二个人且一共有奇数个人时,第一堆的大小需要减1。


题意:如上图所示:有 n 个球分别在 n 个不同的位置,Alice 和 Bob 依次选择一个球向上移动,上面有球不能越过,谁最后把红球移出谁就赢!
分析:
1、n 为偶数时:问题简化一下,假设全都是黄球,谁把最后一个球移出谁就赢
(a1,a2) (a3,a4) …… ( a(2*n-1) , a(2*n) ) ……(a(n-1),an)其中第 i 个球与第 i+1 个球是相邻的,i 为基数,谁面对这个状态谁就必输。
理由很简单,先手移动第 i 个球,后手移动第 i+1 个球,使之仍然保持必赢状态。
回到原问题谁先移出红球谁就赢,假设红球不是第一个球(因为第一个球Alice直接就赢了)
很显然如果红球在偶数位置后手必赢,如果在基数 i 位置,则只需将 第 i-1 个球移到第一个位置就ok了。
所以与红球位置无关。至于产生这个状态(a1,a2) (a3,a4) …… ( a(2*n-1) , a(2*n) ) ……(a(n-1),an),那么就是简单的 Nim问题了
2、n 为基数时:假设红球是第1、2个球。
(a1) (a2,a3)(a4,a5)…… (a(2*n),a(2*n+1)) …… (a(n-1),an) 谁面对这个状态必赢!理由是先手直接把 a1 取走然后就变成上面的情况了,
如果红球在第 2 个位置那么就是必输状态。


#include <cstdio>
#include <cstring>
const int MAXN = 1005;
int a[MAXN];

int main()
{
    int n, k;
    while(~scanf("%d%d", &n, &k)){
        for(int i=1;i<=n;++i){
            scanf("%d", &a[i]);
        }
        if(k == 1){
            printf("Alice\n");
        }
        else{
            int g;
            if(n & 1){
                g = a[1] - (k == 2);//
                for(int i=2;i<n;i+=2){
                    g ^= (a[i+1] - a[i] - 1);
                }
            }else{
                g = 0;
                for(int i=1;i<n;i+=2){
                    g ^= (a[i+1] - a[i] - 1);
                }
            }
            if(g){
                printf("Alice\n");
            }else{
                printf("Bob\n");
            }
        }
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值