題目一:hdu1846
問題描述
各位勇敢者要玩的第一個遊戲是什麼呢?很簡單,它是這樣定義的:
1、 本遊戲是一個二人遊戲;
2、 有一堆石子一共有n個;
3、 兩人輪流進行;
4、 每走一步可以取走1…m個石子;
5、 最先取光石子的一方爲勝;
如果遊戲的雙方使用的都是最優策略,請輸出哪個人能贏。
巴什博弈
只有一堆n個物品,雙方輪流從這堆裏取出物品,規定每次至少取1個,最多取m個,先取完者獲勝。
1.如果物品n=m+1,如果先手取x個,那麼後手每次取m+1-x個,那麼後手必贏。
2.如果物品n=a*(m+1),每回合依然,先手取x個,後手每次取m+1-x個,a回合結束後,還是後手必贏。
3.如果物品n=a*(m+1)+b,此時先手先考慮,如果他採取最優策略,他可以先取b個石子,這樣他把n=a*(m+1)的局面扔給後手,此刻後手成爲了第二種情況的先手,他成爲了第二種情況的後手,這個時候的後手必贏。
規律
所以我們發現了,如果n%(m+1)==0,先手必敗,否則的話,後手必勝。
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
int n,m,t;
cin>>t;
while(t--)
{
cin>>n>>m;
int mm = n%(m+1);
if(mm==0)
{
cout<<"second"<<endl;
}
else
{
cout<<"first"<<endl;
}
}
return 0;
}
稍微變形下
它就是:兩個人輪流報數,每次至少報一個,最多報m個,誰能報到大於等於n的人獲勝。
當然本質是一樣的
第二題:hdu2188
題目描述:
他決定通過捐款來決定兩人誰能入選。
選拔規則如下:
1、最初的捐款箱是空的;
2、兩人輪流捐款,每次捐款額必須爲正整數,並且每人每次捐款最多不超過m元(1<=m<=10)。
3、最先使得總捐款額達到或者超過n元(0<n<10000)的一方爲勝者,則其可以親赴災區服務。
我們知道,兩人都很想入選志願者名單,並且都是非常聰明的人,假設林隊先捐,請你判斷誰能入選最後的名單?
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
int n,m,t;
cin>>t;
while(t--) //目標爲n元
{
cin>>n>>m;
if(n%(m+1)==0)
{
cout<<"Rabbit"<<endl;
}
else
{
cout<<"Grass"<<endl;
}
}
return 0;
}
PN分析
此類題目的通用方法:
P點: 即必敗點,某玩家位於此點,只要對方無失誤,則必敗;
N點: 即必勝點,某玩家位於此點,只要自己無失誤,則必勝。
我們可以打表遞推找些規律
第三題:hdu2147
題目描述:
Recently kiki has nothing to do. While she is bored, an idea appears in his mind, she just playes the checkerboard game.The size of the chesserboard is nm.First of all, a coin is placed in the top right corner(1,m). Each time one people can move the coin into the left, the underneath or the left-underneath blank space.The person who can’t make a move will lose the game. kiki plays it with ZZ.The game always starts with kiki. If both play perfectly, who will win the game?
對於一個nm的地圖,兩人輪流從地圖右上角開始移動硬幣,每次只能朝下走,朝左走,或者左下走,最先不能移動的人失敗。
很明顯,從右上角走到左下角共需要n-1+m-1步,朝左走或者朝右走是走了一步,朝左下的方向走是走了兩步。
似乎,這個題可以轉換成一模一樣的巴士博弈:一共要走(n+m-2)步,每人最少走一步,最多走兩步,先走完的人獲勝。
但是這樣考慮不對,因爲如果走到地圖的最下邊或者最左邊,剩下只能朝下走或者朝右走。
所以我們可以通過pn分析問題
- 首先玩家位於左下角時,此刻他不能移動,此刻他是必敗態P
1 | 2 | 3 | 4 | 5 | 6 | |
---|---|---|---|---|---|---|
1 | ||||||
2 | ||||||
3 | ||||||
4 | ||||||
5 | p |
2.再考慮(5 ,2)點,當玩家A位於(5,2)點時,玩家B只能往左走到達必敗點(5,1),說明此刻這個點,是必勝點N,同理可以推出(5,3)點,同理可以推出第五行和第一列的所有座標的狀態。
1 | 2 | 3 | 4 | 5 | 6 | |
---|---|---|---|---|---|---|
1 | p | |||||
2 | n | |||||
3 | p | |||||
4 | n | |||||
5 | p | n | p | n | p | n |
3.然後我們此刻只能去推(4,2)點的狀態,因爲只有這個點的三個方向的狀態是確定的(4,1)(5,1)(5,2),如果玩家A位於(4,2)點,那麼玩家B移動硬幣的話,肯定要想辦法把玩家A引導到一個必敗狀態,而且他有的選,那就是移動到(5,1)所以(4,2)點就是必勝點。
然後也找出了遞推順序,先左下角,再兩邊。
1 | 2 | 3 | 4 | 5 | 6 | |
---|---|---|---|---|---|---|
1 | p | n | p | n | p | n |
2 | n | n | n | n | n | n |
3 | p | n | p | n | p | n |
4 | n | n | n | n | n | n |
5 | p | n | p | n | p | n |
4.規律,當n和m都爲奇數時必敗,反之必勝
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
int n,m;
while(cin>>n>>m)
{
if(n==0 && m==0)
{
break;
}
if(n&1 && m&1)
{
cout<<"What a pity!"<<endl;
}
else
{
cout<<"Wonderful!"<<endl;
}
}
return 0;
}
第四題:hdu2149
題目描述:
要種田得有田纔行,Lele聽說街上正在舉行一場別開生面的拍賣會,拍賣的物品正好就是一塊20畝的田地。於是,Lele帶上他的全部積蓄,衝往拍賣會。
後來發現,整個拍賣會只有Lele和他的死對頭Yueyue。
通過打聽,Lele知道這場拍賣的規則是這樣的:剛開始底價爲0,兩個人輪流開始加價,不過每次加價的幅度要在1~N之間,當價格大於或等於田地的成本價 M 時,主辦方就把這塊田地賣給這次叫價的人。
Lele和Yueyue雖然考試不行,但是對拍賣卻十分精通,而且他們兩個人都十分想得到這塊田地。所以他們每次都是選對自己最有利的方式進行加價。
由於Lele字典序比Yueyue靠前,所以每次都是由Lele先開始加價,請問,第一次加價的時候,
Lele要出多少才能保證自己買得到這塊地呢?
本題目包含多組測試,請處理到文件結束(EOF)。每組測試佔一行。
每組測試包含兩個整數M和N(含義見題目描述,0<N,M<1100)
對於每組數據,在一行裏按遞增的順序輸出Lele第一次可以加的價。兩個數據之間用空格隔開。
如果Lele在第一次無論如何出價都無法買到這塊土地,就輸出"none"。
思路
n=a*(x+m+1-x)+b
當b=0時,必輸,輸出"none"
如果不能一次買到既a!=0,就要加(m+1)的倍數錢,但是最多加m錢,這就矛盾了,所以如果不能一次買到,就是0;
如果能一次買到,出價區間爲[b,m],(哦,這個題面的n和m剛好是反過來的)。
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
int m,n;
while(cin>>m>>n)
{
if(m%(n+1)==0)
{
cout<<"none"<<endl;
}
else
{
int k = m%(n+1);
cout<<k;
if(m/(n+1)==0)
{
for(int m=k+1;m<=n;++m)
{
cout<<" "<<m;
}
}
cout<<endl;
}
}
return 0;
}