OJ-Problems-Source/.ACM-Templates/.Not classified/博弈.cpp
2016-08-13 23:35:41 +08:00

71 lines
2.5 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//威佐夫博弈 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)的异或和