博弈論

巴什博奕

題目:http://acm.hdu.edu.cn/showproblem.php?pid=2149

有n個物品  ,有兩個參與者A,B ,每人每次從中取[1,m]個 ,假設每人都取最優策略, 求誰最後取完物品 假設A先取

   如果n=m+1,顯然不管第一次A取多少個,都沒辦法取完,而剩下的物品B可以一次性取完,所以結果一定是B贏

  因此,我們可以發現一個先取者的必勝策略,即對於n=(m+1)*k+s;  我們令A先取s個,那麼對於B無論他取多少個,A都可以取一個數,使得A取的物品數+上次B取的物品數=m+1   這樣就出現不管B取多少個,到最後總能剩下m+1   所以B一定會輸;

int n,m;
int k=n%(m+1);
if(k==0) cout<<"B Win"<<endl;
else cout<<"A Win"<<endl; 

斐波那契博弈

題目:http://acm.hdu.edu.cn/showproblem.php?pid=2516

有n個物品  ,有兩個參與者A,B ,每人每次從中取[1,上一個人取物品數的2倍]個 ,假設每人都取最優策略, 求誰最後取完物品 假設A先取

應用了斐波那契數列,“Zeckendorf定理”(齊肯多夫定理):任何正整數可以表示爲若干個不連續的Fibonacci數之和。

總結就是一句話:對於n  若是斐波那契數  則A贏; 否則B贏

sg函數參考鏈接:https://blog.csdn.net/yizhangbiao/article/details/51992022

一共有n個物品總結    sg[]==0  爲必輸策略

  1 每次取1~m個   sg[x]=n%(m+1);

  2 每次取任意個   sg[x]=x;

  3每次取一系列不連續中數任意一個 mex(x);

威佐夫博奕(Wythoff Game)

題目:http://acm.hdu.edu.cn/showproblem.php?pid=1527

有兩堆各若干個物品,兩個人輪流從某一堆或同時從兩堆中取同樣多的物品,規定每次至少取一個,多者不限,最後取光者得勝。

關鍵在於判斷 是不是  奇異局勢

是   先手輸   否   先手贏

判斷條件 a==|b-a|*((sqrt(5)+1)/2)

那什麼叫 奇異局勢呢?

對於(ak,bk)(ak ≤ bk ,k=0,1,2,…,n)       ak 是前面沒有出現過的最小的自然數 ,bk=ak+k;

如:(0,0)、(1,2)、(3,5)、(4,7)、(6,10)、(8,13)、(9,15)、(11,18)、(12,20)

性質1.任何自然數都包含在一個且僅有一個奇異局勢中。     由於ak是未在前面出現過的最小自然數,所以有ak > ak-1 ,而 bk= ak + k > ak-1 + k-1 = bk-1 > ak-1 。所以性質1。成立。     2.任意操作都可將奇異局勢變爲非奇異局勢。

尼姆博奕(Nimm Game)

題目:http://acm.hdu.edu.cn/showproblem.php?pid=1850

有m堆各若干個物品,兩個人A,B輪流從某一堆取任意多的物品,規定每次至少取一個,多者不限,最後取光者得勝。

同威佐夫博弈一樣 也是需要判斷奇異局勢   是  先手輸  否  先手贏

判斷條件  

int ans=0,a[1005];
for(int i=1;i<=m;i++)
  {
     cin>>a[i];
     ans^=a[i]; 
   } 
if(ans==0) cout<<"B Win"<<endl;
else cout<<"A Win"<<endl; 

如何將非奇異局勢變爲 奇異局勢   也就是說  A要贏 第一次應怎麼取呢?

原理:m=3 時 對於(55,81,121),55(+)81=102,121-102=19,所以從121中拿走19個物品 就形成了奇異局勢(55,81,102)

else{
	   int cnt=0;
	        for(int i=1;i<=m;i++){
		        int k=ans^a[i];
		        if(k<a[i]) cnt++;
	        } 
	        cout<<cnt<<endl;
           }

sg函數的應用

題目:http://acm.hdu.edu.cn/showproblem.php?pid=1847

#include<iostream>
#include<cstring> 
using namespace std;
int n,arr[15],sg[1005];

int mex(int x){
	if(sg[x]!=-1) return sg[x]; //如果sg[x]不爲-1  直接輸出 
	bool vis[1005];
	for(int i=0;i<1005;i++) 
	  vis[i]=false;//初始化標誌位 
	for(int i=0;i<=10;i++) {
		int temp=x-arr[i];
		if(temp<0) break;
		sg[temp]=mex(temp);//相當於dfs
		vis[sg[temp]]=true;//該值出現過  標記
	}
	for(int i=0;;i++){
		if(!vis[i]) {
			sg[x]=i;break;//當前sg[]集合中未出現的最小的自然數
		}
	}
	return sg[x];
} 

int main(){
	arr[0]=1;
	for(int i=1;i<=10;i++)
	  arr[i]=arr[i-1]*2;//數組arr[]初始化  即每步可選擇的牌數 
	while(cin>>n){
		memset(sg,-1,sizeof(sg));//初始化 因爲sg函數要求的是大於等於0 所以初始化爲-1
		if(mex(n))  cout<<"Kiki"<<endl;//mex 函數是關鍵 
		else cout<<"Cici"<<endl; 
	} 
	return 0;
}

組合博弈 

有n堆石子,每次可以從第1堆石子裏取1顆、2顆或3顆,可以從第2堆石子裏取奇數顆,可以從第3堆及以後石子裏取任意顆……

我們可以把它看作3個子遊戲,第1個子遊戲只有一堆石子,每次可以取1、2、3顆,很容易看出x顆石子的局面的SG值是x%4。               第2個子遊戲也是隻有一堆 石子,每次可以取奇數顆,經過簡單的畫圖可以知道這個遊戲有x顆石子時的SG值是x%2。                             第3個遊戲有n-2堆石子,就是一個Nim遊戲。對於原遊戲的每 個局面,把三個子游戲的SG值異或一下就得到了整個遊戲的SG值,然後就可以根據這個SG值判斷是否有必勝策略以及做出決策了。其實看作3個子遊戲還是保 守了些,乾脆看作n個子遊戲,其中第1、2個子遊戲如上所述,第3個及以後的子游戲都是“1堆石子,每次取幾顆都可以”,稱爲“任取石子游戲”,這個超簡單的遊戲有x顆石子的SG值顯然就是x。    最後的sg值顯然就是三個sg值的異或和

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章