hdu 4315 Climbing the Hill

哎!还是思路啊。。。。。。。。。。。。。。。。。。。。。

解:

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<iostream>
#include<cstdio>
#include<string.h>
#include<algorithm>
using namespace std;

#define N 1005
int sg[N],a[N];

void SG()
{
   for(int i=0;i<N;i++)
    sg[i]=i;
}

int main()
{
    int i,j,n,k,res;
    SG();
    while(~scanf("%d%d",&n,&k))
    {
        res=0;
        for(i=0;i<n;i++)
          scanf("%d",&a[i]);
        if(k==1) { puts("Alice"); continue;  }
        i=1;
        sort(a,a+n);
        if(n&1)
        {
           i++;
           if(k==2) a[0]--;
           res^=sg[a[0]];
        }
        for(i;i<n;i=i+2)
        {
           res^=sg[a[i]-a[i-1]-1];
        }
        if(res) puts("Alice");
        else puts("Bob");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值