题目链接 (Click) (Here)
蒟蒻的人生第一道博弈论。真吉尔难啊。。。。
通常的博弈论写法似乎都是(SG)函数打表猜规律。本蒻其实本来想学一下博弈论的证明的,但后来发现果然还是打表快速又好用。
这个题中的模型可以拆分成(n/2)个游戏。对每个游戏的每堆石子单独打表,求其(SG)函数,可以得到结论:大小为(N)的石子堆,其拆分为两堆后对应的后继(SG)函数(mex)值为(N-1)。最终利用(SG)函数的性质,把每个游戏的(SG)函数合并,就是所求最初状态的(SG)函数。
打表代码:(感谢 @FlashHu 的题解)
#include<cstdio>
#include<bitset>
#include<iostream>
using namespace std;
const int N = 10;
const int M = N + 1;//随便调大小
int ans[M][M];
bitset <M> s[M];
int mex (bitset <M> b) {
int res = 0;
while (b[res]) ++res;
return res;
}
int main () {
for(int i = 2; i <= N; ++i) {
for (int j = 1, k = i - 1; k; ++j, --k) {
s[i].set (ans[j][k] = mex (s[j] | s[k]));//枚举合并
}
}
for (int i = 0; i < N; ++i) printf ("%3d", i); printf ("
");
for (int i = 1; i < N; ++i){//输出矩阵
printf ("%2d:", i);
for (int j = 1; i + j <= N; ++j) {
printf ("%3d", ans[i][j]);
}
printf ("
");
}
for(int i = 1; i <= N; ++i) {//输出对于每一个a,所有c+d=a的(c,d)的SG值集合
printf ("%2d:SG%d ", i, mex (s[i]));
cout << s[i] << endl;
}
return 0;
}
(STD:)
#include <bits/stdc++.h>
using namespace std;
int T, n, x, y, z, cnt, ans;
int main () {
cin >> T;
while (T--) {
ans = 0;
cin >> n; n >>= 1;
while (n--) {
cnt = 0;
cin >> y >> z;
x = (y - 1) | (z - 1);
while (x & 1) {
++cnt;
x >>= 1;
}
ans ^= cnt;
}
puts (ans ? "YES" : "NO");
}
return 0;
}
注意最终求的(cnt),其实也就是把两堆石子的(SG)合并,得到的单个游戏的(SG)函数值。