取石子游戲之巴什博弈
下面這段來自白白の屋的文章的一段:
巴什博弈:只有一堆n個物品,兩個人輪流從這堆物品中取物,規定每次至少取一個,最多取m個。最後取光者得勝。
顯然,如果n=m+1,那麼由於一次最多隻能取m個,所以,無論先取者拿走多少個,後取者都能夠一次拿走剩餘的物品,後者取勝。因此我們發現瞭如何取勝的法則:如果
n=(m+1)r+s,(r為任意自然數,s≤m),那麼先取者要拿走s個物品,如果後取者拿走k(≤m)個,那麼先取者再拿走m+1-k個,結果剩下(m+1)(r-1)個,以後保持這樣的取法,那麼先取者肯定獲勝。總之,要保持給對手留下(m+1)的倍數,就能最後獲勝。那麼這個時候只要n%(m+1)!=0,先取者一定獲勝
這個遊戲還可以有一種變相的玩法:兩個人輪流報數,每次至少報一個,最多報十個,誰能報到100者勝。
分析此類問題主要放法是:P/N分析:
P點:即必敗點,某玩家位於此點,只要對方無失誤,則必敗;
N點:即必勝點,某玩家位於此點,只要自己無失誤,則必勝。
三個定理:
一、所有終結點都是必敗點P(上游戲中,輪到誰拿牌,還剩0張牌的時候,此人就輸了,因為無牌可取);
二、所有一步能走到必敗點P的就是N點;
三、通過一步操作只能到N點的就是P點;
巴什博弈的一個最重要的特徵就是只有一堆。然後就在其中改,要麼在範圍內不規定個數,要麼就規定只能取幾個,再要麼就倒過來,畢竟是最簡單的博弈,程式碼相對而言較短額~
例題1:NYOJ 23(取石子游戲一),巴什博弈,只要n%(m+1)!=0,則先取者一定獲勝。
#include<iostream>
using namespace std;
int main()
{ int n,m,Case;
cin>>Case;
while(Case--)
{ cin>>n>>m;
cout<<(n%(m+1)?"Win":"Lose")<<endl;
}
return 0;
}
例題2:HDU 2149,英語標題額,原來是中文題。反過來就是一樣的啦,只是取數時的規律是:如果M%(N+1)!=0,那麼第一個取的數就是M%(N+1),留給對手的是(N+1)的倍數,還有就是M<N的情況,不說你也懂,開始寫了sort的,但後來一看可以不要,程式碼:
#include<iostream>
using namespace std;
int main()
{ int n,m;
while(scanf("%d%d",&m,&n)!=EOF)
{ int sum=0;
if(m%(n+1)==0) cout<<"none";
else
{ if(m%(n+1)) cout<<m%(n+1); //直接取餘數
if(n>=m)
{ for(int i=m+1;i<=n;i++)
cout<<" "<<i;
}
}
cout<<endl;
}
return 0;
}
題3:HDU 1847,尋找必敗態,n%3=0則Cici贏,否則Kiki贏。
分析:(1)、若是留給Cici的是3,那麼Cici只能取1個或2個,所以再輪到Kiki取的話必贏。
(2)、若是給Cici留下的是3的倍數,假設為3n(n=1,2,3,..),那麼無論Cici取什麼數,剩餘的數一定可以寫成3m+1或者3m+2(m<n)的形式,那麼只要Kiki再取的時候留給Cici的仍然是3的倍數的話,就必勝了。程式碼略。
當然這種題目可以直接先列舉前面幾個數,就能找到規律啦~