博弈问题Word文档下载推荐.docx
《博弈问题Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《博弈问题Word文档下载推荐.docx(15页珍藏版)》请在冰豆网上搜索。
采用适当的方法,可以将非奇异局势变为奇异局势。
假设面对的局势是(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-aj即可。
从如上性质可知,两个人如果都采用正确操作,那么面对非奇异局势,先拿者必胜;
反之,则后拿者取胜。
那么任给一个局势(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,若都不是,那么就不是奇异局势。
然后再按照上述法则进行,一定会遇到奇异局势。
(三)尼姆博奕(NimmGame):
有三堆各若干个物品,两个人轮流从某一堆取任意多的物品,规定每次至少取一个,多者不限,最后取光者得胜。
这种情况最有意思,它与二进制有密切关系,我们用(a,b,c)表示某种局势,首先(0,0,0)显然是奇异局势,无论谁面对奇异局势,都必然失败。
第二种奇异局势是(0,n,n),只要与对手拿走一样多的物品,最后都将导致(0,0,0)。
仔细分析一下,(1,2,3)也是奇异局势,无论对手如何拿,接下来都可以变为(0,n,n)的情形。
计算机算法里面有一种叫做按位模2加,也叫做异或的运算,我们用符号(+)表示这种运算。
这种运算和一般加法不同的一点是1+1=0。
先看(1,2,3)的按位模2加的结果:
1=二进制01
2=二进制10
3=二进制11(+)
———————
0=二进制00(注意不进位)
对于奇异局势(0,n,n)也一样,结果也是0。
任何奇异局势(a,b,c)都有a(+)b(+)c=0。
如果我们面对的是一个非奇异局势(a,b,c),要如何变为奇异局势呢?
假设a<
b<
c,我们只要将c变为a(+)b,即可,因为有如下的运算结果:
a(+)b(+)(a(+)b)=(a(+)a)(+)(b(+)b)=0(+)0=0。
要将c变为a(+)b,只要从c中减去c-(a(+)b)即可。
获胜情况对先取者进行讨论:
异或结果为0,先取者必败,无获胜方法。
后取者获胜;
结果不为0,先取者有获胜的取法。
拓展:
任给N堆石子,两人轮流从任一堆中任取(每次只能取自一堆),取最后一颗石子的人获胜,问先取的人如何获胜?
根据上面所述,N个数异或即可。
如果开始的时候T=0,那么先取者必败,如果开始的时候T>
0,那么只要每次取出石子使得T=0,即先取者有获胜的方法。
【综合一、三给出】
任给N堆石子,两人轮流从任一堆中任取(每次只能取自一堆),规定每方每次最多取K颗,取最后一颗石子的一方获胜.问先取的人如何获胜?
与上面的问题比,这个更复杂一些,我们可以这样做
令Bi=Aimod(K+1)
定义T‘=B1xorB2xor...xorBn
如果T‘=0那么没有获胜可能,先取者必败
如果T’>
0那么必然存在取的方法,使得T‘=0,先取者有获胜的方法
假设对方取了在Ai中取了r<
=K个
如果Ai中剩下的石子多于K那么就在Ai中取走K+1-r个则Bi不变T‘还是0
如果Ai<
=K那么我们需要重新计算Bi和T‘按照上面的方法来做就可以了
Nyoj23取石子
(一)
时间限制:
3000
ms
|
内存限制:
65535
KB
难度:
2
描述
一天,TT在寝室闲着无聊,和同寝的人玩起了取石子游戏,而由于条件有限,他/她们是用旺仔小馒头当作石子。
游戏的规则是这样的。
设有一堆石子,数量为N(1<
=N<
=1000000),两个人轮番取出其中的若干个,每次最多取M个(1<
=M<
=1000000),最先把石子取完者胜利。
我们知道,TT和他/她的室友都十分的聪明,那么如果是TT先取,他/她会取得游戏的胜利么?
输入
第一行是一个正整数n表示有n组测试数据
输入有不到1000组数据,每组数据一行,有两个数N和M,之间用空格分隔。
输出
对于每组数据,输出一行。
如果先取的TT可以赢得游戏,则输出“Win”,否则输出“Lose”(引号不用输出)
样例输入
10001
1100
样例输出
Lose
Win
#include<
stdio.h>
intmain()
{
intn,N,M;
scanf("
%d"
&
n);
while(n--)
{
scanf("
%d%d"
N,&
M);
if(N%(M+1)==0)
printf("
Lose\n"
);
else
Win\n"
}
return0;
}
Nyoj135取石子
(二)
5
小王喜欢与同事玩一些小游戏,今天他们选择了玩取石子。
游戏规则如下:
共有N堆石子,已知每堆中石子的数量,并且规定好每堆石子最多可以取的石子数(最少取1颗)。
两个人轮流取子,每次只能选择N堆石子中的一堆,取一定数量的石子(最少取一个),并且取的石子数量不能多于该堆石子规定好的最多取子数,等哪个人无法取子时就表示此人输掉了游戏。
假设每次都是小王先取石子,并且游戏双方都绝对聪明,现在给你石子的堆数、每堆石子的数量和每堆石子规定的单次取子上限,请判断出小王能否获胜。
第一行是一个整数T表示测试数据的组数(T<
100)
每组测试数据的第一行是一个整数N(1<
N<
100),表示共有N堆石子,随后的N行每行表示一堆石子,这N行中每行有两个数整数m,n表示该堆石子共有m个石子,该堆石子每次最多取n个。
(0<
=m,n<
=2^31)
对于每组测试数据,输出Win表示小王可以获胜,输出Lose表示小王必然会败。
1
11
提示
注意下面一组测试数据
11
22
正确的结果应该是Win
因为小王会先从第二堆石子中取一个石子,使状态变为
12
这种状态下,无论对方怎么取,小王都能获胜。
intn,a,b,sum,x;
sum=0;
x);
while(x--)
{
scanf("
%d%d"
a,&
b);
sum^=a%(b+1);
}
printf("
%s\n"
sum?
"
Win"
:
Lose"
Nyoj137取石子(三)
1000
6
共有N堆石子,已知每堆中石子的数量,两个人轮流取子,每次只能选择N堆石子中的一堆,取一定数量的石子(最少取一个),取过子之后,还可以将该堆石子中剩下的任意多个石子中随意选取几个放到其它的任意一堆或几堆上。
等哪个人无法取子时就表示此人输掉了游戏。
注意,一堆石子没有子之后,就不能再往此处放石子了。
假设每次都是小王先取石子,并且游戏双方都绝对聪明,现在给你石子的堆数、每堆石子的数量,请判断出小王能否获胜。
例如:
如果最开始有4堆石子,石子个数分别为3142,而小王想决定要先拿走第三堆石子中的两个石子(石子堆状态变为3122),然后他可以使石子堆达到的状态有以下几种:
3122(不再移动石子)
4112(移动到第一堆一个)
3212(移动到第二堆一个)
3113(移动到第四堆一个)
5102(全部移动到第一堆)
3302(全部移动到第二堆)
3104(全部移动到最后)
可能有多组测试数据(测试数据组数不超过1000)
每组测试数据的第一行是一个整数,表示N(1<
=10)
第二行是N个整数分别表示该堆石子中石子的数量。
(每堆石子数目不超过100)
当输入的N为0时,表示输入结束
3
213
1、n=1(n表示石子堆数)时,那么先手必胜;
2、n=2时,若两堆石子数相等,那么先手必败,否则,先手必胜!
比如,先说必败态,两堆石子数相等:
若两堆石子数为[1,1],显然先手必败;
若石子数为[m,m](m>
=2),那么无论先手怎么取,后手都能再将两堆石子数置为相等,最终置成[1,1]的必败状态,所以此时为必败态!
而两堆石子不相等时,先后可以将两堆取相等,那么后手将要面对的就是上述必败态了!
3、n=3时,先手可以先取石子数最多的一堆,同时将另两堆配平,此时后手必败!
4、n=4时,只要有两堆石子数相等,即[x,y,z,z]状态,先手必败,因为后手总能在置出两堆相等的情况来。
。
总结:
n为奇数:
必胜
n为偶数且不存在偶数堆石子数相等的情况:
必胜
否则:
必败
string.h>
intct[103];
intn,i;
while(scanf("
n),n)
intcur;
memset(ct,0,sizeof(ct));
for(i=0;
i<
n;
i++)
cur);
ct[cur]++;
if(n%2!
=0)
continue;
intbz=0;
=100;
if(ct[i]%2!
{
bz=1;
break;
}
if(bz)
Nyoj161取石子(四)
有两堆石子,数量任意,可以不同。
游戏开始由两个人轮流取石子。
游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子;
二是可以在两堆中同时取走相同数量的石子。
最后把石子全部取完者为胜者。
现在给出初始的两堆石子的数目,如果轮到你先取,假设双方都采取最好的策略,问最后你是胜者还是败者。
输入包含若干行,表示若干种石子的初始情况,其中每一行包含两个非负整数a和b,表示两堆石子的数目,a和b都不大于1,000,000,000。
输出对应也有若干行,每行包含一个数字1或0,如果最后你是胜者,则为1,反之,则为0。
21
84
47
math.h>
usingnamespacestd;
intn,m,t;
n,&
m)!
=EOF)
if(n>
m)
t=n;
n=m;
m=t;
t=(int)((1.0+sqrt(5.0))/2.0*(m-n));
%d\n"
t==n?
0:
1);
}
Nyoj358取石子(五)
4
himdd最近很想玩游戏,于是他找到acmj和他一起玩,游戏是这样的:
有一堆石子,两个人轮流从其中取走一定的石子,取走最后所有石子的人为赢家,不过得遵循如下规则:
1.第一次取不能取完,至少取1颗.
2.从第二次开始,每个人取的石子数至少为1,至多为对手刚取的石子数的两倍。
himdd事先想知道自己会不会赢,你能帮帮他吗?
(每次himdd先手)
有多组测试数据,每组有一个整数n(2<
=n<
2^64);
himdd会赢输出Yes,否则输出No;
No
Yes
思路:
结论:
当n为fibonacci数时,先手必败(n>
=2);
证明:
参考资料:
以下为个人思路:
根据“Zeckendorf定理”(齐肯多夫定理):
任何正整数可以表示为若干个不连续的Fibonacci数之和(优先选取最大的)。
比如n=14时,n=8+5+1;
而先手要赢的条件是,先手必须要先取完最小的一堆,再取完次小的一堆,以此类推,并保证每堆的最后一个是自己取到的而且不能取超!
听起来有点绕,举个例子:
1、先看必胜态,n不是fibonacci数的时候,令n=9=5+3+1:
a、先手先取最小的一堆,1个,保证了最后一个是自己取得;
b、此时后手开始取次小的一堆(3个石子),上面说过,要想赢的话,要保证这一堆的第3个必须是自己取得;
此时后手有1个、2个两种取法,但此处无论后手怎么取,都能保证这一堆的最后一个(第3个)是自己取得;
c、此时后手开始取最后一堆(5个石子),b步先手可能取得是1或2,因此此步后手可能有1、2、3、4四种取法,易知后手取3或4的话,自己立马就能取到最后一个,就赢了,而取1或2时,同上(读者自己来),仍能保证取到最后一个(此堆第5个,全局最后一个),必赢!
2、再说必败态,若n本身是fibonacci数时,已经不需要分解了,为方便观察,令n=5:
实际上与上面相同,每一堆(有fib个石子)在自己先手的情况下,无论怎么取,后手都能保证自己取到这一堆的最后一个,因此后手必胜!
PS:
所以上面说总石子数为非fibonacci数时先手必胜的原因在于,总数目分解为若干fib数时,先手先全部取走最小的一堆1个(也是取到了这一堆的最后一个),以后每堆(有fib[i]个石子)都是后手先取,先手便有把握取到这一堆的最后一个了
#defineN100
longlongfib[N];
voidFib()
fib[0]=0;
fib[1]=1;
for(inti=2;
N;
fib[i]=fib[i-1]+fib[i-2];
Fib();
longlongn;
while(~scanf("
%lld"
n))
intp=0;
for(inti=2;
100;
if(fib[i]==n)
p=1;
if(p==1)
No\n"
Yes\n"
Nyoj585取石子(六)
最近TopCoder的PIAOYI和HRDV很无聊,于是就想了一个游戏,游戏是这样的:
有n堆石子,两个人轮流从其中某一堆中任意取走一定的石子,最后不能取的为输家,注意:
每次只能从一堆取任意个,可以取完这堆,但不能不取。
假设PIAOYI先取石子,请你帮他判断他是否能赢(假设他们取的过程中不发生失误,他们足够聪明
)。
第一行输入n,代表有n组测试数据(n<
=10000)
以下每组测试数据包含两行:
第一行:
包含一个整数m,代表本组测试数据有m(m<
=1000)堆石子;
:
第二行:
包含m个整数Ai(Ai<
=100),分别代表第i堆石子的数量。
若PIAOYI赢输出“PIAOYI”,否则输出“HRDV”注意每组结果占一行。
3811
510
HRDV
PIAOYI
intN,a;
N);
while(N--)
intn;
if(!
n)
HRDV\n"
intsum=0;
while(n--)
a);
sum^=a;
sum)
PIAOYI\n"
取石子(七)
Yougth和Hrdv玩一个游戏,拿出n个石子摆成一圈,Yougth和Hrdv分别从其中取石子,谁先取完者胜,每次可以从中取一个或者相邻两个,Hrdv先取,输出胜利着的名字。
输入包括多组测试数据。
每组测试数据一个n,数据保证int范围内。
输出胜利者的名字。
Hrdv
Yougth
intn;
n)!
=3)
Yougth\n"
Hrdv\n"