記得大二的時候還研究過,后來一直放着也沒弄過。 不過今天做TC的時候自己分析到了博弈,發現怎么都不記得了。復習一下.......... 以后工作可能要與Java 打交道了,所以 ......還是練練Java實現吧....
尋找平衡狀態(也稱必敗態, 奇異局勢),(滿足:任意非平衡態經過一次操作可以變為平衡態)
(一)巴什博奕(Bash Game):
只有一堆n個物品,兩個人輪流從這堆物品中取物,規定每次至少取一個,最多取m個.最后取光者得勝.
n = (m+1)r+s , (r為任意自然數,s≤m), 即n%(m+1) != 0, 則先取者肯定獲勝
import java.util.Scanner; public class Main { /* * 誰先取到n-1誰就贏了 */ public static final double Gold = ((1+ Math.sqrt(5.0))/2.0); public static void main(String[] args) { Scanner in = new Scanner(System.in); int n, k; while (in.hasNext()) { n = in.nextInt(); k = in.nextInt(); if (n == 0 && k == 0) break; if ((n - 1) % (k + 1) != 0) System.out.println("Tang"); else System.out.println("Jiang"); } } }
(二)威佐夫博奕(Wythoff Game):
有兩堆各若干個物品,兩個人輪流從某一堆或同時從兩堆中取同樣多的物品,規定每次至少取一個,多者不限,最后取光者得勝.
(ak,bk)(ak ≤ bk ,k=0,1,2,...,n)表示奇異局勢
求法:
ak =[k(1+√5)/2], bk= ak + k (k=0,1,2,...,n 方括號表示取整函數)
判斷:
Gold=(1+sqrt(5.0))/2.0;
1)假設(a,b)為第k種奇異局勢(k=0,1,2...) 那么k=b-a;
2)判斷其a==(int)(k*Gold),相等則為奇異局勢
(注:采用適當的方法,可以將非奇異局勢變為奇異局勢.
假設面對的局勢是(a,b)
若 b = a,則同時從兩堆中取走 a 個物體,就變為了奇異局勢(0,0);
1. 如果a = ak,
1.1 b > bk, 那么,取走b - bk個物體,即變為奇異局勢(ak, bk);
1.2 b < bk 則同時從兩堆中拿走 ak – a[b – ak]個物體,變為奇異局勢( a[b – ak] , a[b – ak]+ b - ak);
2 如果a = bk ,
2.1 b > ak ,則從第二堆中拿走多余的數量b – ak
2.2 b < ak ,則 若b = aj (j < k) 從第一堆中拿走多余的數量a– bj; (a > bj)
若b = bj (j < k) 從第一堆中拿走多余的數量a– aj; ( a > aj)
)
pku 1067
import java.util.Scanner; public class Main { public static final double Gold = ((1+ Math.sqrt(5.0))/2.0); public static void main(String[] args) { Scanner in = new Scanner(System.in); Integer a = 0,b = 0; while (in.hasNext()) { a = in.nextInt(); b = in.nextInt(); if (a > b) { int t; t = a; a = b; b = t; } int k = b - a; if ((int)(k*Gold) == a) System.out.println(0); else System.out.println(1); } } }
(三)尼姆博奕(Nimm Game):
有n堆各若干個物品,兩個人輪流從某一堆取任意多的物品,規定每次至少取一個,多者不限,最后取光者得勝.
任何奇異局勢(a1, a2, … , an)都有a1(+)a2(+)…(+)an =0. ( (+)為 按位與)
例題:pku 2234

import java.util.Scanner; public class Main { public static final double Gold = ((1+ Math.sqrt(5.0))/2.0); public static void main(String[] args) { Scanner in = new Scanner(System.in); int n, k; while (in.hasNext()) { n = in.nextInt(); int ans = 0; for (int i = 0; i < n; ++i) { k = in.nextInt(); ans ^= k; } if (ans == 0) System.out.println("No"); else System.out.println("Yes"); } } }

import java.util.Scanner; public class Main { /* * 首先我們考慮所有棋子都相鄰的情況,這種情況是一種必敗態,因為不論我回退多少個格子,對方總能跟上來,然后我肯定是必敗的。 * 那么怎么判斷我是否能夠使對方到達這個狀態的, Nim判斷的是最后誰取得了最后的棋子,只要利用Nim來判斷我最后是否能夠走完所有空格使對方面對這個狀態即可(也即取得剩下的所有棋子) * 只要對方面對了這個必敗態,那么我就能夠取勝 */ public static void main(String[] args) { Scanner in = new Scanner(System.in); int n,m,a,b; while (in.hasNext()) { n = in.nextInt(); m = in.nextInt(); int ans = 0; for (int i = 0; i < n; ++i){ a = in.nextInt(); b = in.nextInt(); ans ^= (Math.abs((b - a)) - 1); } if (ans != 0) System.out.println("I WIN!"); else System.out.println("BAD LUCK!"); } } }
題解:
剩余1堆石頭:先取者全部拿走,勝利。
剩余2堆石頭:假設兩堆石頭的數量相等,均為m。對於(m,m),先取者沒辦法一次將兩堆石頭拿走,則后取者總可以模仿先取者的動作,只需換一下操作對象,使兩堆石頭變成 (n,n)。最后肯定有某一次先取者操作后石頭只剩下一堆,這時后取者將剩下的一堆全部拿走,勝利。
也就說,對於(m,m),后取者必勝。
如果兩堆石頭不相等,(m,n),則先取者把多的那一堆拿走一些,使兩堆相等(n,n)。
也就說,對於(m,n),先取者必勝。
剩余3堆石頭:(x,y,z)其中(x<=y<=z),先取者可以把z分(y-x)到x上,剩下的全部拿走,於是局面變成了(y,y,0)。也即是說對於3堆石頭,先取者必勝。
對於4堆石頭:如果(m,m,n,n),這樣和兩堆石頭的(m,m)局面是類似的,先取者必敗,后取者必勝。但如果不是這樣的情況,(a,b,c,d)其中(a<=b<=c<=d),先取者操作最后一堆石 頭,總可以使局面變成(a,c,c,a)。這樣就把必敗局留給了后取者,也就是說這種情況先取者必勝。
對於n堆石頭,分析和前面類似。。。。
總的來說,對於n堆石頭:
如果n是奇數,先取者必勝。
如果n是偶數,石頭堆排序后可以寫成(a,a,b,b,c,c,d,d,....)則先取者必敗,否則先取者必勝

import java.util.Arrays; import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); int n = 0; int[] num = new int[101]; while (in.hasNext()) { Arrays.fill(num, 0); n = in.nextInt(); if (n == 0) break; int[] a = new int[n]; for (int i = 0; i < n; ++i){ a[i] = in.nextInt(); } if ((n&1) == 1) { System.out.println("1"); } else { for (int i = 0; i < n; ++i) { num[a[i]]++; } int ans = 0; for (int i = 0; i < n; ++i) { if ((num[a[i]] & 1) != 0) { ans = 1; break; } } System.out.println(ans); } } } }
例題:pku 1704

大意:Georigia和Bob玩棋子游戲,棋子排成一行,類似於落入x坐標上,最左端 為原點0所在位置,棋子i坐落於x軸上的chess[i](chess[i]>0)位置, 游戲規則如下: 1.每人每次可以且只能移動一個棋子 2.每次移動棋子的格子數不限 3.只能向左移動棋子 4.移動過程中不能覆蓋或越過其它棋子 5.當誰沒有棋子可以移動時,就輸了 6.兩人輪流移動石子,女士優先,Georigia總是先移動 7.最左端所在坐標為0,亦即放在1位置的棋子不能再往左移 假設兩人一樣聰明,給定棋子的初始位置,判斷誰將是贏家。 分析: 1.將棋子按位置升序排列,從前往后將其兩兩組成一對。 如果棋子數為奇數,則假設0位置上放有一棋子,與原先位置最小的棋子組成 一對,剩下的依舊從前往后兩兩組成一對. 2.從(1)的分組中取出兩組相鄰的棋子,假設其位置分別為 (chess[i],chess[i+1]),(chess[i+2],chess[i+3]), 此時若我們移動chess[i+2]若干步,對手總能移動chess[i+3]相同步數 故這里chess[i+1]與chess[i+2]之間有多少空格對結局並沒有影響,即 每組棋子的前一個與其前面一組的后一個棋子所在位置的距離對結局並沒有影響 故本題我們只需考慮同一組棋子間的距離 3.對(2)建模,假設現已將n組棋子分為s=(n+1)/2組,每組前后兩棋子間 的距離分別為dis[0],dis[1],dis[2]...dis[s-1], 那么現在問題就演變為有s堆石子,其中第i堆石子個數為dis[i](0<=i<s), 兩人輪流取石子,每次可選任意一堆石子取,且每次至少取一個,多者不限,最后取光者取勝。 現在就是最裸的Nimm Game問題了,套用模型即可. import java.util.Arrays; import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); int T = in.nextInt(); while ((T--) != 0) { int n = in.nextInt(); int[] a = new int[n]; int ans = 0; for (int i = 0; i < n; ++i) a[i] = in.nextInt(); Arrays.sort(a); //for (int i = 0; i < n; ++i) System.out.println(a[i]); if((n&1) == 1) { ans ^= (a[0] - 0 - 1); for (int i = 2; i < n; i += 2) { ans ^= (a[i] - a[i - 1] - 1); } } else { for (int i = 1; i < n; i += 2) { ans ^= (a[i] - a[i - 1] - 1); } } //System.out.println(ans); if (ans == 0) { System.out.println("Bob will win"); } else { System.out.println("Georgia will win"); } } } }
例題:pku 1082 (大量分析… 結論很簡單。 也可以根據簡單的推論模擬實現。)