附上几个参考学习的博客:
http://blog.csdn.net/ac_gibson/article/details/41624623
http://www.cnblogs.com/Su-Blog/archive/2012/08/26/2657696.html
【1】巴什博弈
(1)只有一堆n个物品,两个人轮流从这堆物品中取物,规定每次至少取一个,最多取m个。最后取光者得胜。
判断条件:
n=(1+m)*r+s;
if(n%(1+m)!=0) 先手取最后一次,否则后手取最后一次;
例题:hdu 1846
Brave Game
Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 13300 Accepted Submission(s): 8990
Problem Description
十年前读大学的时候,中国每年都要从国外引进一些电影大片,其中有一部电影就叫《勇敢者的游戏》(英文名称:Zathura),一直到现在,我依然对于电影中的部分电脑特技印象深刻。
今天,大家选择上机考试,就是一种勇敢(brave)的选择;这个短学期,我们讲的是博弈(game)专题;所以,大家现在玩的也是“勇敢者的游戏”,这也是我命名这个题目的原因。
当然,除了“勇敢”,我还希望看到“诚信”,无论考试成绩如何,希望看到的都是一个真实的结果,我也相信大家一定能做到的~
各位勇敢者要玩的第一个游戏是什么呢?很简单,它是这样定义的:
1、 本游戏是一个二人游戏;
2、 有一堆石子一共有n个;
3、 两人轮流进行;
4、 每走一步可以取走1…m个石子;
5、 最先取光石子的一方为胜;
如果游戏的双方使用的都是最优策略,请输出哪个人能赢。
Input
输入数据首先包含一个正整数C(C<=100),表示有C组测试数据。
每组测试数据占一行,包含两个整数n和m(1<=n,m<=1000),n和m的含义见题目描述。
Output
如果先走的人能赢,请输出“first”,否则请输出“second”,每个实例的输出占一行。
Sample Input
2
23 2
4 3
Sample Output
first
second
Author
lcy
Source
ACM Short Term Exam_2007/12/13
Recommend
lcy | We have carefully selected several similar problems for you: 1847 1850 1848 2147 1849
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n,m;
cin>>n>>m;
int s=n%(1+m);
if(s!=0)
{
cout<<"first"<<endl;
}
else
{
cout<<"second"<<endl;
}
}
}
(2)、只有一堆n个物品,两个人轮流从这堆物品中取物,规定每次至少取p个,最多取q个,如果剩下的物品数小于p的话需要一次取完。最后取光者得胜。
判断条件:
n=(p+q)*r+s;
if(s!=0&&s<=p) 先手取最后一次,否则,后手取最后一次;
例题:hdu 2897
邂逅明下
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 5221 Accepted Submission(s): 2428
Problem Description
当日遇到月,于是有了明。当我遇到了你,便成了侣。
那天,日月相会,我见到了你。而且,大地失去了光辉,你我是否成侣?这注定是个凄美的故事。(以上是废话)
小t和所有世俗的人们一样,期待那百年难遇的日食。驻足街头看天,看日月渐渐走近,小t的脖子那个酸呀(他坚持这个姿势已经有半个多小时啦)。他低下仰起的头,环顾四周。忽然发现身边竟站着位漂亮的mm。天渐渐暗下,这mm在这街头竟然如此耀眼,她是天使吗?站着小t身边的天使。
小t对mm惊呼:“缘分呐~~”。mm却毫不含糊:“是啊,500年一遇哦!”(此后省略5000字….)
小t赶紧向mm要联系方式,可mm说:“我和你玩个游戏吧,赢了,我就把我的手机号告诉你。”小t,心想天下哪有题目能难倒我呢,便满口答应下来。mm开始说游戏规则:“我有一堆硬币,一共7枚,从这个硬币堆里取硬币,一次最少取2枚,最多4枚,如果剩下少于2枚就要一次取完。我和你轮流取,直到堆里的硬币取完,最后一次取硬币的算输。我玩过这个游戏好多次了,就让让你,让你先取吧~”
小t掐指一算,不对呀,这是不可能的任务么。小t露出得意的笑:“还是mm优先啦,呵呵~”mm霎时愣住了,想是对小t的反应出乎意料吧。
她却也不生气:“好小子,挺聪明呢,要不这样吧,你把我的邮箱给我,我给你发个文本,每行有三个数字n,p,q,表示一堆硬币一共有n枚,从这个硬币堆里取硬币,一次最少取p枚,最多q枚,如果剩下少于p枚就要一次取完。两人轮流取,直到堆里的硬币取完,最后一次取硬币的算输。对于每一行的三个数字,给出先取的人是否有必胜策略,如果有回答WIN,否则回答LOST。你把对应的答案发给我,如果你能在今天晚上8点以前发给我正确答案,或许我们明天下午可以再见。”
小t二话没说,将自己的邮箱给了mm。当他兴冲冲得赶回家,上网看邮箱,哇!mm的邮件已经到了。他发现文本长达100000行,每行的三个数字都很大,但是都是不超过65536的整数。小t看表已经下午6点了,要想手工算出所有结果,看来是不可能了。你能帮帮他,让他再见到那个mm吗?
Input
不超过100000行,每行三个正整数n,p,q。
Output
对应每行输入,按前面介绍的游戏规则,判断先取者是否有必胜策略。输出WIN或者LOST。
Sample Input
7 2 4
6 2 4
Sample Output
LOST
WIN
Source
2009 Multi-University Training Contest 10 - Host by NIT
Recommend
gaojie | We have carefully selected several similar problems for you: 2896 2898 2894 2893 2891
Statistic | Submit | Discuss | Note
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int main()
{
int n,p,q;
while(~scanf("%d%d%d",&n,&p,&q))
{
int s=n%(p+q);
if(s<=p&&s!=0)
{
cout<<"LOST"<<endl;
}
else
{
cout<<"WIN"<<endl;
}
}
}
(3)一种变相的问法:两个人轮流报数,每次至少报一个,最多报十个,谁能报到100 者胜。
【2】威佐夫博弈 谁面对奇异局势谁输,如果最后取完者胜利的话;
(1)有两堆各若干的物品,两人轮流从其中一堆取至少一件物品,至多不限,或从两堆中同时取相同件物品,规定最后取完者胜利。
若两堆物品的初始值为(x,y),且x<y,则另z=y-x;
记w=(int)[((sqrt(5)+1)/2)*z ];
若w=x,则先手必败,否则先手必胜。
//double k=(sqrt(5.0)+1.0)/2.0;
//int w=(int)(k*z);
//w=x,后手取完了(最后取完);
hdu 1527
取石子游戏
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 8289 Accepted Submission(s): 4656
Problem Description
有两堆石子,数量任意,可以不同。游戏开始由两个人轮流取石子。游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子;二是可以在两堆中同时取走相同数量的石子。最后把石子全部取完者为胜者。现在给出初始的两堆石子的数目,如果轮到你先取,假设双方都采取最好的策略,问最后你是胜者还是败者。
Input
输入包含若干行,表示若干种石子的初始情况,其中每一行包含两个非负整数a和b,表示两堆石子的数目,a和b都不大于1,000,000,000。
Output
输出对应也有若干行,每行包含一个数字1或0,如果最后你是胜者,则为1,反之,则为0。
Sample Input
2 1
8 4
4 7
Sample Output
0
1
0
Source
NOI
Recommend
LL | We have carefully selected several similar problems for you: 1404 1536 1517 1524 1729
Statistic | Submit | Discuss | Note
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
int main()
{
long long int a,b;
while(~scanf("%lld%lld",&a,&b))
{
if(a<b)
{
long long int temp=a;
a=b;
b=temp;
}
long long int t=a-b;
long long int w=(int)(((sqrt(5)+1)/2)*t);
if(w==b)
{
printf("0\n");
}
else
{
printf("1\n");
}
}
}
(2)给你一个局面,让你求先手输赢,假设先手赢的话输出他第一次的取法。
hdu 2177
取(2堆)石子游戏
Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 3035 Accepted Submission(s): 1878
Problem Description
有两堆石子,数量任意,可以不同。游戏开始由两个人轮流取石子。游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子;二是可以在两堆中同时取走相同数量的石子。最后把石子全部取完者为胜者。现在给出初始的两堆石子的数目,如果轮到你先取,假设双方都采取最好的策略,问最后你是胜者还是败者。如果你胜,你第1次怎样取子?
Input
输入包含若干行,表示若干种石子的初始情况,其中每一行包含两个非负整数a和b,表示两堆石子的数目,a和b都不大于1,000,000,且a<=b。a=b=0退出。
Output
输出也有若干行,如果最后你是败者,则为0,反之,输出1,并输出使你胜的你第1次取石子后剩下的两堆石子的数量x,y,x<=y。
如果在任意的一堆中取走石子能胜同时在两堆中同时取走相同数量的石子也能胜,先输出取走相同数量的石子的情况.
Sample Input
1 2
5 8
4 7
2 2
0 0
Sample Output
0
1
4 7
3 5
0
1
0 0
1 2
Author
Zhousc
Source
ECJTU 2008 Summer Contest
Recommend
lcy | We have carefully selected several similar problems for you: 2176 2180 1536 1850 1729
Statistic | Submit | Discuss | Note
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int main()
{
long long int a,b;
while(scanf("%lld%lld",&a,&b)&&a&&b)
{
double k=(sqrt(5.0)+1.0)/2.0;
long long int t=b-a;
if((int)(t*k)==a)
{
printf("0\n");
}
else
{
printf("1\n");
for(long long int i=1; i<=a; i++)
{
long long int m=a-i;
long long int n=b-i;
long long int t1=n-m;
if((int)(t1*k)==m)
{
printf("%lld %lld\n",m,n) ;
break;
}
}
for(long long int i=b-1; i>=0; i--)
{
long long int n=a;
long long int m=i;
if(m<n)
{
long long int temb=m;
m=n;
n=temb;
}
long long int t2=m-n;
if((int)(t2*k)==n)
{
printf("%lld %lld\n",n,m) ;
break;
}
}
}
}
}
【3】尼姆博弈和反尼姆博弈
尼姆博弈指的是这样一个博弈游戏:有任意堆物品,每堆物品的个数是任意的,双方轮流从中取物品,每一次只能从一堆物品中取部分或全部物品,最少取一件,取到最后一件物品的人获胜。
结论就是:把每堆物品数全部异或起来,如果得到的值为0,那么先手必败,否则先手必胜。
hdu 1907
John
Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Others)
Total Submission(s): 5337 Accepted Submission(s): 3085
Problem Description
Little John is playing very funny game with his younger brother. There is one big box filled with M&Ms of different colors. At first John has to eat several M&Ms of the same color. Then his opponent has to make a turn. And so on. Please note that each player has to eat at least one M&M during his turn. If John (or his brother) will eat the last M&M from the box he will be considered as a looser and he will have to buy a new candy box.
Both of players are using optimal game strategy. John starts first always. You will be given information about M&Ms and your task is to determine a winner of such a beautiful game.
Input
The first line of input will contain a single integer T – the number of test cases. Next T pairs of lines will describe tests in a following format. The first line of each test will contain an integer N – the amount of different M&M colors in a box. Next line will contain N integers Ai, separated by spaces – amount of M&Ms of i-th color.
Constraints:
1 <= T <= 474,
1 <= N <= 47,
1 <= Ai <= 4747
Output
Output T lines each of them containing information about game winner. Print “John” if John will win the game or “Brother” in other case.
Sample Input
2
3
3 5 1
1
1
Sample Output
John
Brother
Source
Southeastern Europe 2007
Recommend
lcy | We have carefully selected several similar problems for you: 1913 1908 1914 1915 1909
Statistic | Submit | Discuss | Note
这个题有附加条件,需要另外判断;
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n;
scanf("%d",&n);
int x;
int sum=0;
int flag=0;
int ans=0;
for(int i=1; i<=n; i++)
{
scanf("%d",&x);
sum^=x;
if(x==1)
{
ans++;
}
}
if(ans==n)
{
if(n%2==0)
{
cout<<"John"<<endl;
}
else
{
cout<<"Brother"<<endl;
}
}
else
{
if(sum!=0)
{
cout<<"John"<<endl;
}
else
{
cout<<"Brother"<<endl;
}
}
}
}
【4】斐波那契
有一堆物品,两人轮流取物品,先手最少取一个,至多无上限,但不能把物品取完,之后每次取的物品数不能超过上一次取的物品数的二倍且至少为一件,取走最后一件物品的人获胜。
结论是:先手胜当且仅当n不是斐波那契数(n为物品总数)
hdu 2516
取石子游戏
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 6455 Accepted Submission(s): 3891
Problem Description
1堆石子有n个,两人轮流取.先取者第1次可以取任意多个,但不能全部取完.以后每次取的石子数不能超过上次取子数的2倍。取完者胜.先取者负输出”Second win”.先取者胜输出”First win”.
Input
输入有多组.每组第1行是2<=n<2^31. n=0退出.
Output
先取者负输出”Second win”. 先取者胜输出”First win”.
参看Sample Output.
Sample Input
2
13
10000
0
Sample Output
Second win
Second win
First win
Source
ECJTU 2008 Autumn Contest
Recommend
lcy | We have carefully selected several similar problems for you: 2509 2512 1536 2510 1524
Statistic | Submit | Discuss | Note
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
long long int fib[500];
int main()
{
long long int n;
while(scanf("%lld",&n)&&n)
{
fib[1]=1;fib[2]=1;
for(int i=3; i<50; i++)
{
fib[i]=fib[i-1]+fib[i-2];
}
int flag=1;
for(int i=1; i<50; i++)
{
if(fib[i]==n)
{
flag=0;
break;
}
}
if(flag==0)
{
printf("Second win\n");
}
else if(flag==1)
{
printf("First win\n");
}
}
}
暂且先总结到这里吧;