(一)巴什博奕(Bash Game):只有一堆n个物品,两个人轮流从这堆物品中取物,规
定每次至少取一个,最多取m个。最后取光者得胜。
Link:http://acm.hdu.edu.cn/showproblem.php?pid=1846
Brave Game
Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 7040 Accepted Submission(s): 4717
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
Sample Output
Author
lcy
Source
ACM Short Term Exam_2007/12/13
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
int main()
{
int c,n,m,sum;
scanf("%d",&c);
while(c--)
{
scanf("%d%d",&n,&m);
sum=n%(m+1);
if(sum==0)
printf("second\n");
else
printf("first\n");
}
return 0;
}
Link : http://acm.hdu.edu.cn/showproblem.php?pid=2147
kiki's game
Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 40000/1000 K (Java/Others)
Total Submission(s): 7012 Accepted Submission(s): 4186
Problem Description
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 n*m.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?
Input
Input contains multiple test cases. Each line contains two integer n, m (0<n,m<=2000). The input is terminated when n=0 and m=0.
Output
If kiki wins the game printf "Wonderful!", else "What a pity!".
Sample Input
Sample Output
What a pity!
Wonderful!
Wonderful!
Author
月野兔
Source
HDU 2007-11 Programming Contest
思路:从(1,m)出发,最终到达(n,1),我们可以倒着推,(n,1)是必败态,(n,2),(n-1,1)以及(n-1,2)都是必胜态,这样从左下角往右上角推(只能到达必胜态的状态是必败态,可以到达必败态的状态是必胜态),发现每四个方格都是一样的只有一个必败态,简单的画画就知道了。
然后得出的结论是当n和m为奇数时,右上角为必败态,即当m、n均为奇数时,先移动的只能到达必胜点(即下一个选手取胜),开始处于必败态的选手自然是输。
规律如图可得:
1 2 3 4 5 . . .
1 P N P N P
2 N N N N N
3 P N P N P
4 N N N N N
5 P N P N P
.
.
.
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
int main()
{
int n,m,ans;
while(scanf("%d%d",&n,&m)==2)
{
if(n==0&&m==0)
break;
if((n&1)&&(m&1))
printf("What a pity!\n");
else
printf("Wonderful!\n");
}
return 0;
}
Link: http://acm.hdu.edu.cn/showproblem.php?pid=2149
Public Sale
Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 4574 Accepted Submission(s): 2768
Problem Description
虽然不想,但是现实总归是现实,Lele始终没有逃过退学的命运,因为他没有拿到奖学金。现在等待他的,就是像FarmJohn一样的农田生涯。
要种田得有田才行,Lele听说街上正在举行一场别开生面的拍卖会,拍卖的物品正好就是一块20亩的田地。于是,Lele带上他的全部积蓄,冲往拍卖会。
后来发现,整个拍卖会只有Lele和他的死对头Yueyue。
通过打听,Lele知道这场拍卖的规则是这样的:刚开始底价为0,两个人轮流开始加价,不过每次加价的幅度要在1~N之间,当价格大于或等于田地的成本价 M 时,主办方就把这块田地卖给这次叫价的人。
Lele和Yueyue虽然考试不行,但是对拍卖却十分精通,而且他们两个人都十分想得到这块田地。所以他们每次都是选对自己最有利的方式进行加价。
由于Lele字典序比Yueyue靠前,所以每次都是由Lele先开始加价,请问,第一次加价的时候,
Lele要出多少才能保证自己买得到这块地呢?
Input
本题目包含多组测试,请处理到文件结束(EOF)。每组测试占一行。
每组测试包含两个整数M和N(含义见题目描述,0<N,M<1100)
Output
对于每组数据,在一行里按递增的顺序输出Lele第一次可以加的价。两个数据之间用空格隔开。
如果Lele在第一次无论如何出价都无法买到这块土地,就输出"none"。
Sample Input
Sample Output
Author
Linle
Source
ACM程序设计期末考试——2008-01-02(3 教417)
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
int main()
{
int n,m,ans,i,cnt;
while(scanf("%d%d",&m,&n)!=EOF)
{
if(n>=m)//n<=m先手必胜
{
for(i=m;i<n;i++)
printf("%d ",i);
printf("%d\n",n);
}
else
{
if(m%(n+1)==0)//必败态
{
printf("none\n");
}
else
{
i=1;
cnt=0;
while(i<=n)
{
if((m-i)%(n+1)==0)//必须留下m+1的倍数
{
cnt++;
if(cnt==1)
{
printf("%d",i);
}
else
{
printf(" %d",i);
}
}
i++;
}
puts("");
}
}
}
return 0;
}
Link: http://acm.hdu.edu.cn/showproblem.php?pid=2188
悼念512汶川大地震遇难同胞——选拔志愿者
Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 5829 Accepted Submission(s): 3654
Problem Description
对于四川同胞遭受的灾难,全国人民纷纷伸出援助之手,几乎每个省市都派出了大量的救援人员,这其中包括抢险救灾的武警部队,治疗和防疫的医护人员,以及进行心理疏导的心理学专家。根据要求,我校也有一个奔赴灾区救灾的名额,由于广大师生报名踊跃,学校不得不进行选拔来决定最后的人选。经过多轮的考核,形势逐渐明朗,最后的名额将在“林队”和“徐队”之间产生。但是很巧合,2个人的简历几乎一模一样,这让主持选拔的8600很是为难。无奈,他决定通过捐款来决定两人谁能入选。
选拔规则如下:
1、最初的捐款箱是空的;
2、两人轮流捐款,每次捐款额必须为正整数,并且每人每次捐款最多不超过m元(1<=m<=10)。
3、最先使得总捐款额达到或者超过n元(0<n<10000)的一方为胜者,则其可以亲赴灾区服务。
我们知道,两人都很想入选志愿者名单,并且都是非常聪明的人,假设林队先捐,请你判断谁能入选最后的名单?
Input
输入数据首先包含一个正整数C,表示包含C组测试用例,然后是C行数据,每行包含两个正整数n,m,n和m的含义参见上面提到的规则。
Output
对于每组测试数据,如果林队能入选,请输出字符串"Grass", 如果徐队能入选,请输出字符串"Rabbit",每个实例的输出占一行。
Sample Input
Sample Output
Author
lcy
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
int main()
{
int T,n,m,ans,i,cnt;
while(scanf("%d",&T)!=EOF)
{
while(T--)
{
scanf("%d%d",&n,&m);
if(n%(m+1)==0)
printf("Rabbit\n");
else
printf("Grass\n");
}
}
return 0;
}
理论:(来自:http://www.wutianqi.com/?p=1081)
(二)威佐夫博奕(Wythoff Game):
有两堆各若干个物品,两个人轮流从某一堆或同
时从两堆中取同样多的物品,规定每次至少取一个,多者不限,最后取光者得胜。
这种情况下是颇为复杂的。我们用(ak,bk)(ak ≤ bk ,k=0,1,2,…,n)表示
两堆物品的数量并称其为
局势
,如果甲面对(0,0),那么甲已经输了,这种局势我们
称为
奇异局势
。前几个奇异局势是:(0,0)、(1,2)、(3,5)、(4,7)、(6,
10)、(8,13)、(9,15)、(11,18)、(12,20)。
可以看出,a0=b0=0,ak是未在前面出现过的最小自然数,而 bk= ak + k,奇异局势有
如下三条性质:
1。任何自然数都包含在一个且仅有一个奇异局势中。
由于ak是未在前面出现过的最小自然数,所以有ak > ak-1 ,而 bk= ak + k > ak
-1 + k-1 = bk-1 > ak-1 。所以性质1。成立。
2。任意操作都可将奇异局势变为非奇异局势。
事实上,若只改变奇异局势(ak,bk)的某一个分量,那么另一个分量不可能在其
他奇异局势中,所以必然是非奇异局势。如果使(ak,bk)的两个分量同时减少,则由
于其差不变,且不可能是其他奇异局势的差,因此也是非奇异局势。
3。采用适当的方法,可以将非奇异局势变为奇异局势。
假设面对的局势是(a,b),若 b = a,则同时从两堆中取走 a 个物体,就变为了
奇异局势(0,0);如果a = ak ,b > bk,那么,取走b – bk个物体,即变为奇异局
势;如果 a = ak , b < bk ,则同时从两堆中拿走 ak – ab – ak个物体,变为奇异局
势( ab – ak , ab – ak+ b – ak);如果a > ak ,b= ak + k,则从第一堆中拿走多余
的数量a – ak 即可;如果a < ak ,b= ak + k,分两种情况,第一种,a=aj (j < k)
,从第二堆里面拿走 b – bj 即可;第二种,a=bj (j < k),从第二堆里面拿走 b – a
j 即可。
从如上性质可知,两个人如果都采用正确操作,那么面对非奇异局势,先拿者必胜
;反之,则后拿者取胜。
那么任给一个局势(a,b),怎样判断它是不是奇异局势呢?我们有如下公式:
ak =[k(1+√5)/2],bk= ak + k (k=0,1,2,…,n 方括号表示取整函数)
奇妙的是其中出现了黄金分割数(1+√5)/2 = 1。618…,因此,由ak,bk组成的矩形近
似为黄金矩形,由于2/(1+√5)=(√5-1)/2,可以先求出j=[a(√5-1)/2],若a=[
j(1+√5)/2],那么a = aj,bj = aj + j,若不等于,那么a = aj+1,bj+1 = aj+1
+ j + 1,若都不是,那么就不是奇异局势。然后再按照上述法则进行,一定会遇到奇异
局势。
Link:http://acm.hdu.edu.cn/showproblem.php?pid=1527
取石子游戏
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 4644 Accepted Submission(s): 2457
Problem Description
有两堆石子,数量任意,可以不同。游戏开始由两个人轮流取石子。游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子;二是可以在两堆中同时取走相同数量的石子。最后把石子全部取完者为胜者。现在给出初始的两堆石子的数目,如果轮到你先取,假设双方都采取最好的策略,问最后你是胜者还是败者。
Input
输入包含若干行,表示若干种石子的初始情况,其中每一行包含两个非负整数a和b,表示两堆石子的数目,a和b都不大于1,000,000,000。
Output
输出对应也有若干行,每行包含一个数字1或0,如果最后你是胜者,则为1,反之,则为0。
Sample Input
Sample Output
Source
NOI
Recommend
LL | We have carefully selected several similar problems for you: 1404 1536 1517 1524 1729
AC code(模板参考自http://blog.csdn.net/liwen_7/article/details/7937113):
#include<iostream>
#include<string.h>
#include<math.h>
using namespace std;
int main()
{
int n, m;
int k;
double x=(1+sqrt(5.0))/2;
while(scanf("%d%d",&n,&m)!=EOF && n+m)
{
if(n>m) //如果 n > m,根据奇异局势应该要a<b
{
n^=m; //通过三个异或 交换n、m
m^=n;
n^=m;
}
k=m-n; //显然,遇到奇异局势的人一定会输。
if((int)(k*x)==n)
printf("0\n"); //因为奇异局势变成非奇异局势后,下一个人一定可以将局势
//再次变成奇异局势,所以一定还是第一次遇到奇异局势的人遇到奇异局势,则他定输
else
{
printf("1\n");//碰到非奇异局势,将其变成奇异局势,可能有两种变法。
/*for(int i=1;i<=n;i++)//①:同时从两堆中拿相同数目的石子。
{
int a=n-i , b=m-i;
k=b-a; //cout<<"a= "<<a<<" b= "<<b<<" "<<x<<endl;
if((int)(k*x)==a) printf("%d %d\n",a, b);
}//cout<<"111111111111"<<endl;
for(int i=m-1;i>=0;i--)//②:从一堆中取。
{
int a=n , b=i;
if(a > b) //同理,保持奇异局势中 a < b
{
a^=b;
b^=a;
a^=b;
}
k=b-a;
if((int)(k*x)==a) printf("%d %d\n",a , b);
}//cout<<"22222222222222"<<endl;*/
}
}
return 0;
}
Link:http://acm.hdu.edu.cn/showproblem.php?pid=2177
取(2堆)石子游戏
Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1584 Accepted Submission(s): 959
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
Sample Output
Author
Zhousc
Source
ECJTU 2008 Summer Contest
AC code:
#include<iostream>
#include<string.h>
#include<math.h>
using namespace std;
int main()
{
int n, m;
int k;
double x=(1+sqrt(5.0))/2;
while(scanf("%d%d",&n,&m)!=EOF && n+m)
{
if(n>m) //如果 n > m,根据奇异局势应该要a<b
{
n^=m; //通过三个异或 交换n、m
m^=n;
n^=m;
}
k=m-n; //显然,遇到奇异局势的人一定会输。
if((int)(k*x)==n)
printf("0\n"); //因为奇异局势变成非奇异局势后,下一个人一定可以将局势
//再次变成奇异局势,所以一定还是第一次遇到奇异局势的人遇到奇异局势,则他定输
else
{
printf("1\n");//碰到非奇异局势,将其变成奇异局势,可能有两种变法。
for(int i=1;i<=n;i++)//①:同时从两堆中拿相同数目的石子。
{
int a=n-i , b=m-i;
k=b-a; //cout<<"a= "<<a<<" b= "<<b<<" "<<x<<endl;
if((int)(k*x)==a) printf("%d %d\n",a, b);
}//cout<<"111111111111"<<endl;
for(int i=m-1;i>=0;i--)//②:从一堆中取。
{
int a=n , b=i;
if(a > b) //同理,保持奇异局势中 a < b
{
a^=b;
b^=a;
a^=b;
}
k=b-a;
if((int)(k*x)==a) printf("%d %d\n",a , b);
}//cout<<"22222222222222"<<endl;
}
}
return 0;
}
(三)尼姆博奕(Nimm Game): 有三堆各有若干个物品,两个人轮流从某一堆取任意多的物品,规定每次至少取一个,多者不限,最后取光者得胜。
用(a , b , c)表示某种局势,首先(0, 0 , 0)显然是奇异局势(即后取得人一定取光物品),第二种奇异局势是(0 , n , n),只要与对手拿走同样多的物品,最后都将导致(0 , 0 , 0)。第三种(1 , 2 , 3)也是奇异局势,无论对手如何拿,接下来都可以变成(0 , n , n)的情况。
用(+)表示异或(也叫按位模2加)运算,对于任何奇异局势,a1 (+) a2 (+) ……(+) an = 0。
取火柴的游戏
题目1:今有若干堆火柴,两人依次从中拿取,规定每次只能从一堆中取若干根, 可将一堆全取走,但不可不取,最后取完者为胜,求必胜的方法。
题目2:今有若干堆火柴,两人依次从中拿取,规定每次只能从一堆中取若干根, 可将一堆全取走,但不可不取,最后取完者为负,求必胜的方法。
定义:若所有火柴数异或为0,则该状态被称为利他态,用字母T表示;否则, 为利己态,用S表示。
[定理1]:对于任何一个S态,总能从一堆火柴中取出若干个使之成为T态。
[定理2]:T态,取任何一堆的若干根,都将成为S态。
[定理 3]:S态,只要方法正确,必赢。
[定理4]:T态,只要对方法正确,必败。
接着来解决第二个问题。
定义:若一堆中仅有1根火柴,则被称为孤单堆。若大于1根,则称为充裕堆。
定义:T态中,若充裕堆的堆数大于等于2(若充裕堆数为1,则最后异或一定不为0 ,因为高位只有一个1,异或结果一定不为0),则称为完全利他态,用T2表示;若充裕堆的堆数等于0,则称为部分利他态,用T0(说明孤单堆的数目一定是偶数,否则异或结果定不为0,且因为是偶数,后拿的一定会最后取光所有物品,在第二个问题中先拿的是赢家)表示。
孤单堆的根数异或只会影响二进制的最后一位,但充裕堆会影响高位(非最后一位)。一个充裕堆,高位必有一位不为0,则所有根数异或不为0。故不会是T态。
[定理5]:S0态,即仅有奇数个孤单堆,必败。T0态必胜。
[定理6]:S1态( 一堆充裕堆,x个孤单堆),只要方法正确,必胜。
证明:
若此时孤单堆堆数为奇数,把充裕堆取完 , 变成S0;
否则有偶数个孤单堆,将充裕堆取得剩一根。这样,就变成奇数个孤单堆即 S0,由对方取。
由定理5 ,必胜。
[定理7]:S2态不可转一次变为T0态。
[定理8]:S2态可一次转变为T2态。
(若充裕堆>2 (1:充裕堆为偶数,孤单堆为奇数,将一个孤单堆取完),(2:充裕堆为奇数,若孤单堆为偶数,则取完一个充裕堆,若孤单堆为奇数,则取得一个充裕堆剩1个。))
[定理9]:T2态,只能转变为S2态或S1态。( 若充裕堆>2则可转变为S2 ,若充裕堆=2,则可转变为S1)。
[定理10]:S2态,只要方法正确,必胜.
证明:
方法如下:
1) S2态(①),就把它变为T2态(②)。(由定理8)
2) 对方只能T2转变成S2态或S1态(①)(定理9)
若转变为S2, 转向1)
若转变为S1(①), 这己必胜。(定理5)
定理11]:T2态必输。
综上所述,若是 S2,S1,T0 。 则先下的人必胜。
若是 T2,S0 。则先下的人必输。
两题比较:
第一题(全过程): S2 ->T2 ->S2->T2->... ...->T2->S1->T0(偶数个孤单堆)->...->S0->T0 (全0)。
第二题(全过程):S2->T2->S2->T2-> …… ->T2->S1->S0(技术个孤单堆)->T0->S0->……->S0->T0(全0)
S1态可以转变为S0态(第二题做法),也可以转变为
T0(第一题做法)。哪一方控制了S1态,他即可以有办法使自己得到最后一根(转变为
T0),也可以使对方得到最后一根(转变为S0)。
所以,抢夺S1是制胜的关键!
为此,始终把T2态让给对方,将使对方处于被动状态,他早晚将把状态变为S1.
Link:http://acm.hdu.edu.cn/showproblem.php?pid=1907
John
Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Others)
Total Submission(s): 3740 Accepted Submission(s): 2112
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
Sample Output
Source
Southeastern Europe 2007
Recommend
lcy | We have carefully selected several similar problems for you: 1913 1908 1914 1915 1909
编程思想:
这一系列博弈类型可以概括为:
巴什博弈:从一堆石子中拿石子,一次拿1~m个。
威佐夫博弈:从两堆石子中拿石子,方法①任选一堆石子拿k个石子(k≥1),方法②从两堆石子中拿相同数量的石子(当然所拿的数要≥1)
尼姆博弈:从三堆石子中拿石子,每次任选一堆拿任意数目(≥1)的石子。
这些当然是谁先那光,谁获胜。
尼姆博弈解法,和二进制有关。
反正我不知道他们怎么推得,只会用。o(╯□╰)o。。。
给出的数 用异或加起来,若等于0,则为奇异态(必胜态)。
这道题有些不一样,如果John吃的是某个盒子最后一颗,那就判定John为败。
所以,这道题分为两种情况讨论:
①若所有堆的数量都为1。则根据奇偶来判断谁胜。
②其他情况,将所有数据异或起来,判断是否为奇异态。
AC code:
#include <iostream>
#include <algorithm>
using namespace std;
int arr[48];
int main()
{
int t,n,i,temp;
cin>>t;
while( t-- )
{
cin>>n;
for(i=0;i<n;++i)
cin>>arr[i];
sort(arr,arr+n);
// 如果全是1,按照奇偶判断谁获胜
if( arr[n-1]==1 )
{
if( n&1 ) cout<<"Brother"<<endl;
else cout<<"John"<<endl;
continue;
}
// 异或加起来
temp=arr[0]^arr[1];
for(i=2;i<n;++i)
temp^=arr[i];
if( temp==0 ) cout<<"Brother"<<endl;
else cout<<"John"<<endl;
}
return 0;
}