mirror of
https://github.com/Kiritow/OJ-Problems-Source.git
synced 2024-03-22 13:11:29 +08:00
71 lines
2.5 KiB
C++
71 lines
2.5 KiB
C++
//威佐夫博弈 Wizov game
|
||
//有两堆各若干个物品, 两个人轮流从某一堆或同时从两堆中取同样多的物品, 规定每次至少取一个, 多者不限, 最后取光者得胜
|
||
//ak = [k(1 + √ 5) / 2], bk = ak + k (k = 0, 1, 2, ..., n 方括号表示取整函数)
|
||
const double gs = (sqrt(5.0) + 1.0) / 2.0;
|
||
bool Wizov(ll a, ll b) {
|
||
return min(a, b) == (ll)(abs(a - b) * gs);
|
||
}
|
||
//威佐夫博弈 1 <= N <= 1e18
|
||
const int N = 95; //~95 for 1e18
|
||
ll fib[N] = { 0, 1 }; //预处理fibonacci数列
|
||
bool s[N];
|
||
bool Wizov(ll a, ll b) {
|
||
int w = upper_bound(fib + 1, fib + N, a) - fib - 1, pos = 1; ll ret = 0;
|
||
for (int i = w; i > 0; i--) {
|
||
if (a >= fib[i]) { s[i] = true; a -= fib[i]; }
|
||
else { s[i] = false; }
|
||
}
|
||
while (!s[pos]) { pos++; }
|
||
for (int i = pos & 1 ? w - 2 : w; i >= 0; i--) {
|
||
if (s[i]) { ret += fib[i + 1]; }
|
||
}
|
||
return ret == b;
|
||
}
|
||
//尼姆博奕 Nimm Game
|
||
//有三堆各若干个物品, 两个人轮流从某一堆取任意多的物品, 规定每次至少取一个, 多者不限, 最后取光者得胜
|
||
//计算从1 - n范围内的SG值
|
||
//Array存储可以走的步数, Array[0]表示可以有多少种走法
|
||
//Array[]需要从小到大排序
|
||
//HDU1847 博弈SG函数
|
||
//1.可选步数为1-m的连续整数,直接取模即可,SG(x) = x % (m + 1); (即巴什博奕 Bash game)
|
||
//2.可选步数为任意步,SG(x) = x;
|
||
//3.可选步数为一系列不连续的数,用SG(x)计算
|
||
int sg[N];
|
||
bool Hash[N];
|
||
int SG(int Array[], int n) {
|
||
memset(sg, 0, sizeof(sg));
|
||
for (int i = 0; i <= n; ++i) {
|
||
memset(Hash, 0, sizeof(Hash));
|
||
for (int j = 1; j <= Array[0]; j++) {
|
||
if (i < Array[j]) { break; }
|
||
Hash[sg[i - Array[j]]] = true;
|
||
}
|
||
for (int j = 0; j <= n; j++) {
|
||
if (!Hash[j]) { sg[i] = j; break; }
|
||
}
|
||
}
|
||
return sg[n];
|
||
}
|
||
//带输出方案
|
||
int a[N], ans[N][2]; //a[]为各堆石子数量
|
||
void printNim(int n) { //石子堆数
|
||
int cnt = 0, ret = 0;
|
||
for (int i = 0; i < n; i++) { ret ^= a[i]; }
|
||
for (int i = 0; i < n; i++) {
|
||
if (a[i] > (ret ^ a[i])) {
|
||
ans[cnt][0] = a[i]; ans[cnt][1] = s ^ a[i];
|
||
cnt++;
|
||
}
|
||
}
|
||
if (cnt) { //判断先手是胜是负
|
||
puts("Yes");
|
||
printf("%d\n", cnt); //输出使先手为胜的方案的数目
|
||
for (int i = 0; i < cnt; i++) {
|
||
printf("%d %d\n", ans[i][0], ans[i][1]); //输出若先手为胜的走法
|
||
}
|
||
} else { puts("No"); }
|
||
}
|
||
//树上删边游戏
|
||
//给定一棵n个点的有根树, 每次可以删掉一个子树
|
||
//则叶子节点的SG值为0, 非叶子节点的SG值为其所有孩子节点(SG值 + 1)的异或和
|