博弈论初步学习
一.巴什博弈(Bash Game):
首先我們來玩一個比較古老的報數游戲。A和B一起報數,每個人每次最少報一個,最多報4個。輪流報數,看誰先報到30.
如果不知道巴什博弈的可能會覺得這個是個有運氣成分的問題,但是如果知道的人一定知道怎樣一定可以贏。
比如A先報數的話,那么B一定可以贏(這里假定B知道怎么正確的報數)
B可以這樣報數,每次報5-k(A)個數,其中k(A)是A報數的個數.
這樣的話每一次兩人報完數之后會變成5 10 15 20 25 30,這樣是不是B一定會贏呢?是不是有一種被欺騙的感覺呢?
好吧下面我們來看看這個原理。
我們先看下一個一眼就能看出答案的例子 比如說我們報到5(4+1),每次報最多報4個,最少報1個.那么是不是后者一定可以
贏呢?答案是肯定的。好了到這巴什博弈的精髓基本就OK了。
那么如果我們要報到n+1,每次最多報n個,最少報1個的話,后者一定能夠贏?,F在我們需要報數到n,而每次最多報數m個,最
少報數1個.我們可以化成這樣n = k*(1+m)+r(0 <= r <= m)這樣的話如果r不等于0那么先手一定會贏,為什么呢?
首先先手報r個,那么剩下k倍(1+m)個數,那么我們每次報數1+m-k(B)個數就一定能保證最后剩下1+m個,那么就到了上面我
們說的那個了,先手就一定會贏,如果r=0那么后手一定會贏,道理一樣的。到這巴什博弈也就介紹完了,知道這個道理之后我
們也可以去騙小朋友了.
代碼如下:
?
題目:http://acm.hdu.edu.cn/showproblem.php?pid=4764
題意:兩個玩家,給定兩個數n和k,先手先寫一個數X,接下來另一個人寫一個數滿足條件1<= X-Y <=k,如果誰寫的數不小于
n,那么這個人就必敗,問是先手必勝還是先手必敗?
結論:如果(n-1)%(k+1)=0,那么先手必敗,否則必勝.
二.威佐夫博弈(Wythoff?Game):
這種博弈比前面一種要稍微復雜一點。我們來看下下面這個游戲。
有兩堆火柴棍,每次可以從某一堆取至少1根火柴棍(無上限),或者從兩堆取相同的火柴棍數,最后取完的是勝利者。
好了,如果你不知道這個博弈定理,對于小數目的火柴棍數,可能還能推出來,但是如果火柴棍數一多,就不行了。
看了下面的這個介紹,你也會有一種被騙的感覺。
首先我們知道兩堆火柴是沒有差別的,也就是說第一堆有a根,第二堆有b根和第一堆有b根,第二堆有a根是一樣的結果。
我們用一個二維的狀態(a,b)來記錄當前剩下的火柴數,表示第一堆剩下a根火柴,第二堆剩下b根火柴。同樣我們假設兩個
人的編號是A和B,且A先取。
那么如果某個人遇到了這樣的狀態(0,0)那么也就是說這個人輸了。這樣的狀態我們叫做奇異狀態,也可以叫做失敗態。
那么接下來的幾個失敗態為(1,2),(3,5),(4,7),(6,10),(8,13)……
我們用a[i]表示失敗態中的第一個,b[i]表示失敗態中的第二個.(i從0開始).
那么我們可以看到b[i]?=?a[i]+i;(i?>=?0),a[i]是前面的失敗態中沒有出現過的最小的整數
下面我們可以得到三個基本的結論。
?
1.每個數僅包含在一個失敗態中
?? ?
首先我們知道a[k]是不可能和前面的失敗態中的a[i],b[i]重復的(這點由a[i]的得到可以道)b[k]?=?a[k]+k?>?a[k-
1]+k>a[k-1]+k-1+1>a[k-1]+(k-1)?=?b[k-1]>a[k-1]這樣我們知道每個數僅在一個失敗態中。
??
2.每個失敗態可以轉到非失敗態。
????
加入當前的失敗態為(a,b),那么如果我們只在一堆中取的話,肯定會變成非失敗態(這點由第一點可以保證),如果從兩堆同
時取的話,由于每個失敗態的差是不一樣的,所以也不可能得到一個失敗態。也就是說一個失敗態不管你怎么取,都會得到一
個非失敗態。
??
3.每個非失敗態都可以轉到一個失敗態
????
對于這個結論,首先我們要知到每個狀態(a,b)要么a?=?a[i],要么b?=?b[i].(每個數都出現在一個失敗態中),下面我們
分兩種情況來討論:
????
I.a?=?a[i].如果b?=?a的話那么一次取完就變成了(0,0).如果b?>?b[i]的話,那么我們從第二堆中取走b-b[i]就變成了一個失敗態。如果b?<?b[i],那么我們從兩堆中同時取走a-a[b-a[i]]這樣得到失敗態(a[b-a[i]],a[b-a[i]]+b-a[i])(a[i]?=?a)
???
II.b?=?b[i].如果a?>?a[i]那么我們從第一堆中取走a-a[i]根火柴.?如果a?<?a[i].這里又分兩種情況。第一是
a?=?a[k](k?<?i)那么我們從第二堆取走b?-?b[k]就行了。第二是a?=?b[k]這樣的話由于兩堆火柴是沒有區別的,所以
我們把b變成a[k]就行了,也即是從第二堆火柴中取走b-a[k]就變成了失敗態至于怎么判斷一個狀態是否是失敗態.我們
可以用下面的方法來判斷a[i]?=?[i*(1+√5)/2](這里的中括號表示向下取整)b[i]=a[i]+i;?那么這就是一個失敗態.
代碼如下:
#include <stdio.h> #include <math.h> #include <iostream> using namespace std;int main() {int num1, num2, tmp; //第一堆剩的數量為num1,第二堆剩num2while(scanf("%d%d", &num1, &num2) != EOF){if(num1 > num2)swap(num1, num2);tmp = floor((num2 - num1) * (1 + sqrt(5.0)) / 2.0); //黃金分割if(tmp == num1) printf("Lose\n"); //奇異局勢必敗else printf("Win\n");}return 0; }
三.尼姆博弈(Nim Game):
?
指的是這樣的一個博弈游戲,目前有任意堆石子,每堆石子個數也是任意的,雙方輪流從中取出石子,規則如下:
1)每一步應取走至少一枚石子;每一步只能從某一堆中取走部分或全部石子;
2)如果誰取到最后一枚石子就勝。也就是尼姆博弈(Nimm Game)。
必敗局面:也叫奇異局勢。無論做出何出操作,最終結果都是輸的局面。必敗局面經過2次操作后,可以達到另一個必敗局面。
必勝局面:經過1次操作后可以達到必敗局面。
即當前局面不是必敗局面就是必勝局面,而必勝局面可以一步轉變成必敗局面。
最終狀態:
(1)最后剩下一堆石子;(必勝局面)
(2)剩下兩堆,每堆一個;(必敗局面)
(3)當石子剩下兩堆,其中一堆只剩下1顆,另一堆剩下多于n顆石子時,當前取的人只需將多于1顆的那一堆取出n-1顆,則局面變為剛才提到的必敗局面。(必勝局面)
判斷當前局勢是否為必勝(必敗)局勢:
1)把所有堆的石子數目用二進制數表示出來,當全部這些數按位異或結果為0時當前局面為必敗局面,否則為必勝局面;
2)在必勝局面下,因為所有數按位異或的結果是大于零的,那么通過一次取,將這個(大于其它所有數按位異或的結果
的)數下降到其它所有數按位異或的結果,這時局面就變為必敗局面了。
定理:一組自然數中必然存在一個數,它大于等于其它所有數按位異或的結果。
證明:原命題等價于,設a1^a2^...^an=p,p≠0時,必存在k,使得ak^p<ak(當p=0時,對于任意的k,有ak^p=ak)。
設p的最高位是第q位,則至少存在一個k,使得ak的第q位也是1,而ak^p的第q位為0,所以ak^p<ak
補綴一點,(a^b)^b=a^(b^b)=a^0=a,所以ak^p相當于“其它所有數按位異或的結果”。
例1:2 45 45
45^45=0,45和45的異或等于0。
例 2:3 3 6 9
局勢(3,6,9)因為3^6^9不等于0,所以這是一個必勝局勢。
3 011
^6 110
5 101
即從第3堆中的9個中取走9-5=4個,則(3,6,9)->(3,6,5),3^6^5=0,故(3,6,5)為奇異局勢,即從必勝
局勢轉變成必敗局勢。
代碼如下:
?
#include<iostream> using namespace std; int temp[ 20 ]; //火柴的堆數int main() {int i, n, min;while( cin >> n ){for( i = 0; i < n; i++ )cin >> temp[ i ]; //第i個火柴堆的數量min = temp[ 0 ];for( i = 1; i < n ; i++ )min = min^temp[ i ]; //按位異或if( min == 0 )cout << "Lose" << endl; //輸elsecout << "Win" << endl; //贏}return 0; }
總結
- 上一篇: HDU1403(后缀数组--最长公共子串
- 下一篇: 最短路径之Floyd算法