题目一: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;
}