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; }