Nim及SG函数

HDU 4315

题目:山上有n个人,每个人给出距离山顶的距离,给出其中一个人为king,每次能挑选一个人向上移动,不能越过其他人,最后将king移动到山顶者获胜。问获胜者。

思路:转化为NIM游戏。

简记:

NIM游戏:有n堆石子,每次可以选择一堆拿走任意数量的石子,不能拿石子的一方失败。

NIM游戏的必败态为所有堆的石子数目异或值为0的情况,那是因为,如果异或值不为0,设其为x,x的二进制表示中的最左边的一位1必然存在一堆(设为z)与之对应,将这一堆取成y=x^z,那么得到的状态为异或值为0(必败态),而必败态不论如何取,后继状态必然为必胜态。(具体参考《算法竞赛入门经典训练指南》)

本题与NIM游戏的关系与转化。

如果k=1,那么显然先手必胜。

先考虑一个简化问题:不考虑king,当不能移动时为败。

首先注意到一个必败:如果n为偶数,且所有奇数位的人与其后的人均紧靠,那么此时为必败态,因为不论先手如何移动(只可能移动奇数位),后手必然可以移动偶数位使其依然保持紧靠。如果加入考虑king,那么k为偶数时,同上讨论,先手必败,如果k为奇数,那么可以把游戏看为移动k-1位置的人到1(由前讨论,后手可以达到这一目标),故k为奇数时仍然为先手必败态。

此时我们就可以考虑如何将游戏进行至必败态,如果先手移动奇数位,那么后手总可以移动偶数位使得相隔距离不变,所以问题可以只考虑奇数位和其后相邻的偶数位的间距,把他们看成NIM堆,移动对应减少石子,当石子数为0时就是紧靠的必败态。

当n为奇数时,如果k不等于2,那么可以把第一个人到山顶看成一堆石子,当NIM游戏结束时第一个人到达山顶,情况就变得和偶数时一样。

如果k等于2,那么第一个人到山顶就是必败态,故第一个人最多能到1的位置,第一堆石子数减一。

主要思路参考kuangbin大神:http://www.cnblogs.com/zhourongqing/archive/2012/07/28/2613679.html

#include <iostream>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <functional>
#include <set>
#include <cmath>
#define pb push_back
#define fs first
#define se second
#define sq(x) (x)*(x)
#define eps 0.0000000001
#define IINF (1<<30)
using namespace std;
typedef long long ll;
typedef pair<ll,ll> P;
const int maxv=1005;
int n,k;
int a[maxv];
int main(){
    freopen("/home/files/CppFiles/in","r",stdin);
    /*    std::ios::sync_with_stdio(false);
        std::cin.tie(0);*/
    while(cin>>n>>k){
        for(int i=0;i<n;i++){
            scanf("%d",a+i);
        }
        if(k==1){
            puts("Alice");
            continue;
        }
        if(n%2==0){
            int res=0;
            for(int i=1;i<n;i+=2){
                res^=a[i]-a[i-1]-1;
            }
            if(res==0){
                puts("Bob");
            }else{
                puts("Alice");
            }
        }else{
            int res=0;
            if(k==2){
                res^=a[0]-1;
            }else{
                res^=a[0];
            }
            for(int i=2;i<n;i+=2){
                res^=a[i]-a[i-1]-1;
            }
            if(res==0){
                puts("Bob");
            }else{
                puts("Alice");
            }
        }
    }
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/Cw-trip/p/4693519.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值