Nim游戏:有物体若干堆,可以是火柴棍或是围棋子等等均可。两个人轮流从堆中取物体若干,规定最后取光物体
者取胜。
假设有三堆物品(a,b,c);我们定义谁面对都是失败的局势称为奇异局势。比如(0,0,0),(0,n,n)都是
是一个奇异局势,(1,2,3)也可以变为奇异局势,无论对手怎么拿,都可以变成(0,n,n)的局势。
通过很多种奇异局势的观察,发现所有的奇异局势的连续亦或值都等于0(证明有点迷)。
问题来了,如何把一个非奇异局势变为奇异局势呢,假设现在的局势为(a,b,c),定义(+)为亦或加,(-)
为亦或减,那么只要把c变成 a(+)b即可,要实现这一操作只需要在 c 中(-)去 a(+)b即可,例如:
例1、(14,21,39),14(+)21=27,39(-)27=12,所以从39中拿走12个物体即可达
到奇异局势(14,21,27)。
例2、(55,81,121),55(+)81=102,121(-)102=19,所以从121中拿走19个物品
就形成了奇异局势(55,81,102)。
例题:HDU - 2176
代码:
#include <map>
#include <set>
#include <cmath>
#include <queue>
#include <deque>
#include <stack>
#include <cstdio>
#include <vector>
#include <iomanip>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#define ll long long
#define mod 10000000007
#define mem(a) memset(a,0,sizeof(a))
using namespace std;
typedef pair <int,int> pii;
const int maxn = 200000+5 , inf = 0x3f3f3f3f;
int a[maxn];
int main(){
int n,ans;
while(scanf("%d",&n)==1&&n){
ans = 0;
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
ans^=a[i];
}
if(ans==0) cout<<"No"<<endl;
else{
cout<<"Yes"<<endl;
for(int i=0;i<n;i++){
//找出应该取出的那一堆
//如果所有堆的亦或值减去这一堆的小于这一堆就说明应该从这一堆中拿
//拿走多出的后这一堆就和其他堆形成了奇异局势
int tmp = ans^a[i];
if(tmp<a[i]) printf("%d %d\n",a[i],tmp);
}
}
}
}