巴什博奕
題目: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值的異或和