古语有云,“笑人情似纸,世事如棋”。生活中每一个人如同棋手,其每个行为如同在一张看不见的棋盘 上布子,精明慎重的棋手们相互揣摩、牵制、争赢,下出诸多精彩纷呈、变化无穷的棋局。而什么是博 弈论?就是研究棋手们 的“出棋” 过程,从中抽象出可逻辑化的部分,并将其系统化的一门科学,也是运 筹学的一个重要学科。html
1:博弈模型为两人轮流决策的博弈。而且两人都使用最优策略来取得胜利。ios
2:博弈是有限的。即不管两人如何决策,都会在有限步决出胜负。c++
3:博弈是公平的。即两人进行决策的规则相同。bash
1:bash博弈;2:nim博弈;3:威佐夫博弈;5:Fibonacci博弈;6:sg函数;ide
假设一堆石子有n个,每次最多取m个,甲乙两个玩家轮流取石子,最后把石子取完的人获胜,保证甲乙每一步的决策都是最优的,请问给定n和m,问甲胜仍是乙胜。函数
不妨假设刚刚开始ui
n = m + 1
,那么后手必胜,有以下结论:spa
n = ( m + 1 ) * r + s
其中(r > 1,0 <= s < m + 1)
。若是s=0的话,先手每次取k个,后手只要取(m+1-k)个便可,后手必赢。若是s!=0的话,先手者第一次取s个,后手第一次取k个,接下来先手只要取(m + 1 - k)
个便可,先手必赢。例题:code
#include<bits/stdc++.h> using namespace std; int c, m, n;//总捐款数,每次最多m int main() { //freopen("in.txt","r",stdin); cin >> c; while (c--) { cin >> n >> m; if (n % (m + 1) == 0) cout << "Rabbit\n"; else cout << "Grass\n"; } return 0; }
hdu_1846视频
hdu_1847
假设有n堆石子,每堆石子分别有\(a_1,a_2,…,a_n个\),每次能够选择任意一堆且至少取1枚石子, 甲乙两个玩家轮流取石子, 最后把石子取完的人获胜, 保证甲乙每一步的决策都是最优的, 甲为先手操做, 问甲胜仍是乙胜。
当\(a\)不全为\(0\)时, 任意一个\(res!=0\)的局面, 先手能够经过必定的操做让后手面对\(res=0\)的局面。
对于任意一个\(res=0\)的局面, 先手没法经过任何操做让后手面对\(res=0\)的局面。
得出结论, 当\(res=0\)时先手必败, 反之必胜。
问题描述: 有一个\(n\)级台阶的楼梯, 每级台阶上有若干个石子, 其中第i级台阶上有\(ai\)个石子\((i≥1)\)。两位玩家路轮流操做, 每次操做能够从任意一级台阶上拿若干个石子放到下一级台阶上(不能不拿)。
已经拿到地面的石子不能再拿, 最后没法进行操做的人视为失败。
问若是两人都采起最优策略, 先手是否必胜.
1): 考虑极端状况, 当\(a1,a3,…,an\)全为0时, \(res=0\), 此时先手只能将偶数级台阶往下搬, 后手只须要将先手从偶数级台阶上搬下来的石子所有搬到下一级偶数级台阶, 先手必败。
2): 当\(res=x≠0\)时, 经过经典\(Nim\)游戏的证实, 咱们知道必定有一种方法搬必定的石子到下一级让后手面对res为0的局面。
3):当\(res=x=0\)且\(a\)不全为\(0\)时, 咱们没法经过任何操做让下一个状态的\(res\)也为\(0\)。
即对于\(res\)不为\(0\)的状况, 先手总能经过必定的操做让后手面对\(res\)为\(0\)的状况,。
然而\(res\)为\(0\)时, 先手不管作什么操做都没法让后手面对\(res\)为\(0\)的状况。
那么此刻咱们就将题目转化为在奇数台阶上的经典Nim游戏。
思考题:
为何不用\(res=a_2∧a_4∧a_6∧,…,∧a_n=0\)(n为偶数)来断定胜负?
#include<bits/stdc++.h> using namespace std; const int maxn = 100 + 10; int n, a[maxn], res; int main() { //freopen("in.txt","r",stdin); while (cin >> n,n) { res = 0; for (int i = 1; i <= n; i++){ cin >> a[i]; res ^= a[i]; } if (res == 0) puts("0"); else{ int ans = 0; for (int i = 1; i <= n; i++) if ((res ^ a[i]) < a[i]) ans++; cout << ans << endl; } } return 0; }
#include<bits/stdc++.h> using namespace std; int main() { //freopen("in.txt","r",stdin); int n, m; while (cin >> n >> m) { int res = 0; for (int i = 1; i <= n; ++i) { int a, b; cin >> a >> b; res = res ^ (abs(a - b) - 1); } if (res == 0) puts("BAD LUCK!"); else puts("I WIN!"); } return 0; }
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; const int N = 1000 + 10; int a[N]; int main() { //freopen("in.txt", "r", stdin); int t, n; cin >> t; while (t--) { int ans = 0; cin >> n; for (int i = 1; i <= n; ++i) cin >> a[i]; sort(a + 1, a + n + 1); if (n % 2) { ans ^= (a[1] - 1); for (int i = 3; i <= n; i += 2) ans ^= (a[i] - a[i - 1] - 1); } else for (int i = 2; i <= n; i += 2) ans ^= (a[i] - a[i - 1] - 1); if (ans) printf("Georgia will win\n"); else printf("Bob will win\n"); } return 0; }
#include<bits/stdc++.h> using namespace std; const int maxn = 1e3 + 10; int a[maxn]; int n, k; int main() { //freopen("in.txt","r",stdin); while(cin >> n >> k) { memset(a, 0, sizeof a); for(int i = 1; i <= n; i++) scanf("%d", &a[i]); if(k == 1) puts("Alice"); else { int res = 0; if(n & 1) { if(k == 2) res ^= a[1] - 1; else res ^= a[1]; for(int i = 3; i <= n; i += 2) res ^= a[i] - a[i - 1] - 1; } else { for(int i = 2; i <= n; i += 2) res ^= a[i] - a[i - 1] - 1; } if(res) puts("Alice"); else puts("Bob"); } } return 0; }
两堆石子各有若干个, 两人轮流从一堆取至少一个石子或从两堆取一样多的物品, 最后一名取完石子者胜利。
•首先考虑最(zhao)极(gui)端(lv)的状况, (0, 0), (1, 2), (3, 5)局面为先手必败局面。并且这样的数字对被称为奇异局势。
奇异局势的定义以下:
接下来咱们看奇异局势的几个性质:
性质1: 任何天然数都包含在一个且仅有一个奇异局势中。
性质2: 任意操做都能将奇异局势转变为非奇异局势.
性质3: 采起适当的方法, 可将非奇异局势转变为奇异局势。
证实略
#include<bits/stdc++.h> using namespace std; int n, m; bool check(int n, int m) { int x = min(n, m), y = max(n, m); double c = (sqrt(5.00000) + 1) / 2; double d = (double)(y - x); if(x == int(c*d)) return 1; // 必败 return 0; } void work() { if(n > m) swap(n, m); // (n, m) //第一个模块 咱们能一块儿减去让他成为必败态 { int tem = m - n; double c = (sqrt(5.00000) + 1) / 2; int a = double(tem) * c; int b = a + tem; if(n - a == m - b) cout << a << " " << b << endl; } //第二个模块 咱们求出当前n的奇异局势, 若是m比他大 拿走就行 //若是m比他小咱们求出(x, n) 而后拿走m { double c = (sqrt(5.00000) + 1) / 2; int x = n; double d = x / c; int y = n + d; if(m > y) cout << x << " " << y << endl; else { double x = double(n) * 2 / (sqrt(5.000000) + 1); cout << int(x) << " " << n << endl; } } } int main() { while(cin >> n >> m) { if(!(n + m)) break; if(check(n, m)) puts("0"); else { puts("1"); work(); } } return 0; }
一堆石子有n个,两人轮流取.先取者第1次能够取任意多个,但不能所有取完。之后每次取的石子数不能超过上次取子数的2倍。取完者胜。给定n,问先手必胜仍是必败。
例题:
#include<bits/stdc++.h> using namespace std; typedef long long ll; unordered_map<int, int> mp; ll f[50]; void fib() { f[0] = f[1] = 1; for(int i = 2; i <= 50; i++) { f[i] = f[i - 1] + f[i - 2]; mp[f[i]]++; } } int main() { int n;fib(); while(cin >> n) { if(n == 0)break; if(!mp[n]) puts("First win"); else puts("Second win"); //若是是fibonacci数, 则先手必败 return 0; }
mex运算
的结果。s
的SG函数值
, 即\(SG(G)=SG(s)\)SG函数
为0。#include<bits/stdc++.h> #define ms(a,b) memset(a,b,sizeof a) using namespace std; const int maxn = 1e3 + 100; int n, num; int sg[maxn]; int head[maxn], ver[maxn], nxt[maxn], tot; void add(int u, int v) { ver[++tot] = v, nxt[tot] = head[u], head[u] = tot; } int getSG(int x) { if (sg[x] != -1)return sg[x]; bool vis[maxn] = { false }; for (int i = head[x]; i; i = nxt[i]) { // 扫描全部出边 int y = ver[i]; sg[y] = getSG(y); vis[sg[y]] = true;//全部出边的sg函数值 } for (int i = 0; i < n; ++i) if (!vis[i]) return sg[x] = i;// mex运算 return 0; } void init() { ms(head, 0), ms(sg, -1), ms(ver, 0), ms(nxt, 0); tot = 0; } int main() { //freopen("in.txt", "r", stdin); ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); while (cin >> n) { init(); for (int i = 0; i < n; ++i) { cin >> num; while (num--) { int x; cin >> x; add(i, x); } } while (cin >> num && num) { int res = 0; while (num--) { int x; cin >> x; res ^= getSG(x); } if (res) puts("WIN"); else puts("LOSE"); } } }
#include<bits/stdc++.h> using namespace std; const int maxn = 1e4 + 10; int s[maxn], sg[maxn]; int k; void init() { memset(sg, -1, sizeof(sg)); } int GetSg(int x) { if(sg[x] != -1) return sg[x]; bool vis[maxn]; memset(vis, 0, sizeof(vis)); for(int i = 1; i <= k; i++) if(x >= s[i]) { sg[x - s[i]] = GetSg(x - s[i]); vis[sg[x - s[i]]] = 1; } for(int i = 0; ; i++) if(!vis[i]) return sg[x] = i; return 0; } int main() { ios::sync_with_stdio(false); while(cin >> k) { init(); if(k == 0) break; for(int i = 1; i <= k; i++) cin >> s[i]; int num; cin >> num; while(num--) { int x, res = 0; cin >> x; for(int i = 1; i <= x; i++) { int y; cin >> y; res ^= GetSg(y); } if(res) cout << "W"; else cout << "L"; } cout << endl; } return 0; }