H - 阶梯 - NIM博弈1 POJ - 1704 (hh2048题单)
【题目大意】:
在棋盘上有n个棋子,Bob 与 Georgia 轮流下棋 。Georgia先手(因为她是女生)。
每个选手都可以选择一颗棋子,将棋子往左边移动任意格(这个棋子的左边不能是边界或者越过)
最后能操作的人赢。问谁会赢。
【思路】:
阶梯博弈的思路。
阶梯博弈的关键点(自己当前的想法):
1。要统计出所有的终止位置,棋子到了这个终止位置后不能有其他操作 。
2。所有棋子的位置最后都要落到终止位置。
3。原始状态存在抵消操作: 比如到终止位置的步数可以始终保持偶数,那么后手就可以模仿先手的操作进行抵消,进入到终止状态,那么就不影响游戏结果
4。将会对游戏产生影响的操作进行异或操作,求出最后的答案。如果存在走到奇数的情况,那么后手就不能一直效仿先手的操作,要进行变招,这时候就会影响游戏结果,变成nim游戏。
第4点的解释:如果会产生影响,那么将其看成一个独立游戏,这个独立游戏不会对其他情况产生影响,所以异或他们的sg值。
左边界的下标是0。终止状态就是当棋子移到了0的位置,然后一个个紧贴着排下去。 这显然是终止状态并且是唯一的终止状态,并且最终都会到这个状态。
当两个棋子紧贴的时候,先手如果移动前面的棋子,那么后手只要移动后面的棋子,那么又能回到紧贴的状态,不会对游戏产生影响。
如果一对棋子不是紧贴,那么不管先手将前面的棋子移动多少格,后手都能移动后面的棋子(因为中间不会存在边界,或者有其他棋子的情况),始终能保持住这个状态。
如果先手主动移动一对棋子中后面的那个,那么先手有可能不能保持原来的状态(比如左边就是边界,或者左边存在棋子),会导致游戏产生变化。
每对棋子相当于一次简单游戏,他们间的空隙大小相当于棋子数。
如果这对棋子是紧贴的,那么sg(0) = 0
如果空隙为1格, 那么sg(1) = mex(sg(0)) =1
空隙为2格,sg(2) = mex(sg(0),sg(1)) = 2
所以sg(x) = x。
只需要对所有简单游戏进行异或操作(nim思想),那么就能求出先手必赢还是后手必赢。
【代码】:

#include <bits/stdc++.h> #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <string> #include <vector> #include <stack> #include <bitset> #include <cstdlib> #include <cmath> #include <set> #define ms(a, b) memset(a,b,sizeof(a)) #define fast ios::sync_with_stdio(false); cin.tie(0); cout.tie(0) #define ll long long #define ull unsigned long long #define rep(i, a, b) for(ll i=a;i<=b;i++) #define lep(i, a, b) for(ll i=a;i>=b;i--) #define endl '\n' #define pii pair<int, int> #define pll pair<ll, ll> #define vi vector<ll> #define vpi vector<pii> #define vpl vector<pll> #define mi map<ll,ll> #define all(a) (a).begin(),(a).end() #define gcd __gcd #define pb push_back #define mp make_pair #define lb lower_bound #define ub upper_bound #define ff first #define ss second #define test4(x, y, z, a) cout<<"x is "<<x<<" y is "<<y<<" z is "<<z<<" a is "<<a<<endl; #define test3(x, y, z) cout<<"x is "<<x<<" y is "<<y<<" z is "<<z<<endl; #define test2(x, y) cout<<"x is "<<x<<" y is "<<y<<endl; #define test1(x) cout<<"x is "<<x<<endl; using namespace std; const int N = 1e5 + 10; const int maxx = 0x3f3f3f; const int mod = 1e9 + 7; const int minn = -0x3f3f3f; const int M = 2 * N; ll T, n, m; ll a[N] , d[N] ; void solve() { cin>>n; d[0] = 0; a[0] = 0; rep(i,1,n){ cin>>a[i]; } sort(a+1,a+1+n); int res = 0; for ( int i=n;i>0;i-=2){ res ^= (a[i]-a[i-1]-1); } if( res ) cout<<"Georgia will win"<<endl; else cout<<"Bob will win"<<endl; } int main() { fast; cin >> T; while (T--) { solve(); } return 0; }