古語有云,「笑人情似紙,世事如棋」。生活中每一個人如同棋手,其每個行爲如同在一張看不見的棋盤 上布子,精明慎重的棋手們相互揣摩、牽制、爭贏,下出諸多精彩紛呈、變化無窮的棋局。而什麼是博 弈論?就是研究棋手們 的「出棋」 過程,從中抽象出可邏輯化的部分,並將其系統化的一門科學,也是運 籌學的一個重要學科。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; }