三类经典博弈类型:巴什博奕、威佐夫博奕、尼姆博奕
取石子问题:
A:1堆石子有n个,两人轮流取.先取者第1次可以取任意多个,但不能全部取完.以后每次取的石子数不能超过上次取子数的2倍。取完者胜.先取者负输出"Second win".先取者胜输出"Firstwin".
Input
输入有多组.每组第1行是2<=n<2^31. n=0退出.
Output
先取者负输出"Second win". 先取者胜输出"Firstwin".
参看SampleOutput.
Sample Input
2
13
10000
0
Sample Output
Second win
Second win
First win
这是一道Fibonacci’s Game(斐波那契博弈)
斐波那契博弈模型,大致上是这样的:
有一堆个数为 n 的石子,游戏双方轮流取石子,满足:
1. 先手不能在第一次把所有的石子取完;
2. 之后每次可以取的石子数介于1到对手刚取的石子数的2倍之间(包含1和对手刚取的石子数的2倍)。
约定取走最后一个石子的人为赢家,求必败态。
(转)分析:
n = 2时输出second;
n = 3时也是输出second;
n = 4时,第一个人想获胜就必须先拿1个,这时剩余的石子数为3,此时无论第二个人如何取,第一个人都能赢,输出first;
n = 5时,first不可能获胜,因为他取2时,second直接取掉剩下的3个就会获胜,当他取1时,这样就变成了n为4的情形,所以输出的是second;
n = 6时,first只要去掉1个,就可以让局势变成n为5的情形,所以输出的是first;
n = 7时,first取掉2个,局势变成n为5的情形,故first赢,所以输出的是first;
n = 8时,当first取1的时候,局势变为7的情形,第二个人可赢,first取2的时候,局势变成n为6得到情形,也是第二个人赢,取3的时候,second直接取掉剩下的5个,所以n= 8时,输出的是second;
…………
从上面的分析可以看出,n为2、3、5、8时,这些都是输出second,即必败点,仔细的人会发现这些满足斐波那契数的规律,可以推断13也是一个必败点。
借助“Zeckendorf定理”(齐肯多夫定理):任何正整数可以表示为若干个不连续的Fibonacci数之和。n=12时,只要谁能使石子剩下8且此次取子没超过3就能获胜。因此可以把12看成8+4,把8看成一个站,等价与对4进行"气喘操作"。又如13,13=8+5,5本来就是必败态,得出13也是必败态。也就是说,只要是斐波那契数,都是必败点。
所以我们可以利用斐波那契数的公式:fib[i] = fib[i-1] + fib[i-2],只要n是斐波那契数就输出second。
- #include <stdio.h>
- __int64 f[100];
- int main()
- { __int64 n,m,i;
- while((scanf("%I64d",&n)!=EOF),n)
- {
- f[0]=2;
- f[1]=3;
- for(i=2;i<=44;i++)
- f[i]=f[i-1]+f[i-2];
- for(i=0;i<=44;i++)
- {
- if(n==f[i])
- {
- m=1;
- break;
- }
- m=0;
- }
- if(m==1)
- printf("Second win\n");
- else
- printf("First win\n");
- }
- return 0;
- }
#include <stdio.h>
__int64 f[100];
int main()
{ __int64 n,m,i;
while((scanf("%I64d",&n)!=EOF),n)
{
f[0]=2;
f[1]=3;
for(i=2;i<=44;i++)
f[i]=f[i-1]+f[i-2];
for(i=0;i<=44;i++)
{
if(n==f[i])
{
m=1;
break;
}
m=0;
}
if(m==1)
printf("Second win\n");
else
printf("First win\n");
}
return 0;
}
B:有两堆石子,数量任意,可以不同。游戏开始由两个人轮流取石子。游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子;二是可以在两堆中同时取走相同数量的石子。最后把石子全部取完者为胜者。现在给出初始的两堆石子的数目,如果轮到你先取,假设双方都采取最好的策略,问最后你是胜者还是败者。
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
1、在一堆石子中取走任意多颗;
2、在两堆石子中取走相同多的任意颗;
约定取走最后一颗石子的人为赢家,求必胜策略。
两堆石头地位是一样的,我们用余下的石子数(a,b)来表示状态,并画在平面直角坐标系上。
和前面类似,(0,0)肯定是 P 态,又叫必败态。(0,k),(k,0),(k,k)系列的节点肯定不是 P 态,而是必胜态,你面对这样的局面一定会胜,只要按照规则取一次就可以了。再看 y = x 上方未被划去的格点,(1,2)是 P 态。k > 2 时,(1,k)不是 P 态,比如你要是面对(1,3)的局面,你是有可能赢的。同理,(k,2),(1 + k, 2 + k)也不是 P 态,划去这些点以及它们的对称点,然后再找出 y = x 上方剩余的点,你会发现(3,5)是一个 P 态,如此下去,如果我们只找出 a ≤ b 的 P 态,则它们是(0,0),(1,2),(3,5),(4,7),(6,10)……它们有什么规律吗?
忽略(0,0),很快会发现对于第 i 个 P 态的 a,a = i * (sqrt(5) + 1)/2 然后取整;而 b = a + i。居然和黄金分割点扯上了关系。
前几个必败点如下:(0,0),(1,2),(3,5),(4,7),(6,10),(8,13)……可以发现,对于第k个必败点(m(k),n(k))来说,m(k)是前面没有出现过的最小自然数,n(k)=m(k)+k。
判断一个点是不是必败点的公式与黄金分割有关:
m(k) = k * (1 + sqrt(5))/2
n(k) = m(k) + k;
k=大—小
奇异局势是;int(k*(1+sqrt(5.0))/2)==m
谁面临奇异局势谁败
- #include<stdio.h>
- #include<math.h>
- int main ()
- {
- intn,m,k,t;
- while(~scanf("%d%d",&n,&m))
- {
- if(n<m)
- {
- t=m;
- m=n;
- n=t;
- }
- k=n-m;
- if(int(k*(1+sqrt(5.0))/2)==m)
- printf("0\n");
- else
- printf("1\n");
- }
- return 0;
- }
#include<stdio.h>
#include<math.h>
int main ()
{
intn,m,k,t;
while(~scanf("%d%d",&n,&m))
{
if(n<m)
{
t=m;
m=n;
n=t;
}
k=n-m;
if(int(k*(1+sqrt(5.0))/2)==m)
printf("0\n");
else
printf("1\n");
}
return 0;
}
C.题目大意:有n堆苹果,每堆有mi个。两人轮流取,每次可以从一堆苹果中取任意连续个苹果,最后取光者为输。Fra先下,问是否可以获胜
首先给出结论: 先手胜当且仅当
(1)所有堆石子数都为1且游戏的SG值为0,(2)存在某堆石子数大于1且游戏的SG值不为0
证明:
(1)若所有堆石子数都为1且SG值为0,则共有偶数堆石子,故先手胜。
(2)
i)只有一堆石子数大于1时,我们总可以对该堆石子操作,使操作后石子堆数为奇数且所有堆得石子数均为1
ii)有超过一堆石子数大于1时,先手将SG值变为0即可,且总还存在某堆石子数大于1
因而,先手胜。
- #include<stdio.h>
- int a[105];
- int main()
- {
- inti,ans,n,temp;
- while(scanf("%d",&n)!=EOF)
- {
- temp=0;//孤单堆
- for(i=0;i<n;i++)
- {
- scanf("%d",&a[i]);
- if(i==0)
- ans=a[i];
- else
- ans=ans^a[i];
- if(a[i]>1) temp=1;
- }
- if(temp==0)
- {
- if(n%2==1)
- printf("No\n");
- else
- printf("Yes\n");
- continue;
- }
- if(ans==0)
- printf("No\n");
- else
- printf("Yes\n");} return 0;}
#include<stdio.h>
int a[105];
int main()
{
inti,ans,n,temp;
while(scanf("%d",&n)!=EOF)
{
temp=0;//孤单堆
for(i=0;i<n;i++)
{
scanf("%d",&a[i]);
if(i==0)
ans=a[i];
else
ans=ans^a[i];
if(a[i]>1) temp=1;
}
if(temp==0)
{
if(n%2==1)
printf("No\n");
else
printf("Yes\n");
continue;
}
if(ans==0)
printf("No\n");
else
printf("Yes\n");} return 0;}
D.巴什博弈各位勇敢者要玩的第一个游戏是什么呢?很简单,它是这样定义的:
1、 本游戏是一个二人游戏;
2、 有一堆石子一共有n个;
3、 两人轮流进行;
4、 每走一步可以取走1…m个石子;
5、 最先取光石子的一方为胜;
巴什博弈:
只有一堆n个物品,两个人轮流从这堆物品中取物,规定每次至少取一个,最多取m个.
最后取光者得胜.
如果物品时1~m的话,先手必赢
物品时1+m~2*m的话,不管先手如何取后手必赢
所以如果n=(m+1)*任意倍数+s(s<=m)的话先手必赢,因为先手拿掉s的话,
就成了奇异局势,不管后手取多少(假设是x),
先手只要取m-x-1就一定会使最后石子变成后手面对1+m~2*m的局势这样
;先手必赢,反之后手必赢
- #include<iostream>
- using namespace std;
- int main()
- {
- intc,n,m,i,j;
- scanf("%d",&c);
- while(c--)
- {
- scanf("%d%d",&n,&m);
- if(n%(m+1))
- printf("first\n");
- else
- printf("second\n");
- }
- return0;
- }
#include<iostream>
using namespace std;
int main()
{
intc,n,m,i,j;
scanf("%d",&c);
while(c--)
{
scanf("%d%d",&n,&m);
if(n%(m+1))
printf("first\n");
else
printf("second\n");
}
return0;
}