NYOJ博弈论题目汇总
NYOJ 23 取石子(一)
有一堆石子共有n个,A和B轮流取,A先,每次最少取1个,最多取m个,先取完者胜,A,B足够聪明,问谁先胜?
比较简单的巴什博弈,若n%(m+1)!=0,A胜,否则B胜。
NYOJ 833 取石子(七)
n个石子摆成一圈,A和B轮流取,每次可以从中取一个或相邻两个,先取完者胜,A先取,问谁胜?
若n==1||n==2 则A胜,否则B胜。
NYOJ 161 取石子(四)
两堆石子分别n,m(n>=m)个,A和B轮流取,有两种取法,一是在任意的一堆中取走任意多的石子,最少为一;二是在两堆中同时取走相同数量的石子。A先取,先取完者胜,问A是否胜?(胜输出1,负为0)
著名的威佐夫博奕,
结论:若(n-m)*(sqrt(5.0)+1.0)/2.0!=m ,则A胜,否则负。
01.#include
02.#include
03.#include
04.#include
05.using namespace std;
06.int main()
07.{
08.int a,b;
09.while(scanf("%d%d",&a,&b)!=EOF)
10.{
11.if(a
12.swap(a,b);
13.if(floor((a-b)*((sqrt(5.0)+1.0)/2.0))==b)
14.printf("0\n");
15.else
16.printf("1\n");
17.}
18.}
NYOJ 837 Wyothoff Game
题意同上取石子(四),不过现在要求前n(n<=10W)个必败态,比如A面对(0,0)必败,面对(1,2)(3,5)(4,7)也必败,现在给一个n,求前n个必败态。
由上面取石子(四)的题解链接可知其第n项为(n*(sqrt(5.0)+1.0)/2,n*(sqrt(5.0)+1.0)/2+n),由此打一个10W的表即可。
01.#include
02.#include
03.int a[100001][2];
04.int main()
05.{
06.int i;
07.for(i=1;i<=10000;i++)
08.a[i][0]=i*(sqrt(5.0)+1)/2,a[i][1]=a[i][0]+i;
09.int n;
10.while(scanf("%d",&n)!=EOF)
11.{
12.for(i=0;i<=n;i++)
13.printf("(%d,%d)",a[i][0],a[i][1]);
14.puts("");
15.}
16.}
NYOJ 886 取石子(八)
题意同上取石子(四),依然为威佐夫博弈,但是如果A胜,要你输出你第一次怎么取子,(如果在任意的一堆中取走石子能胜同时在两堆中同时取走相同数量的石子也能胜,先输出取走相同数量的石子的情况,假如取一堆的有多种情况,先输出从石子多的一堆中取的情况,且要求输出结果保证第二个值不小于第一个值。)
首先判断若n==0,则直接输出(0,0);
else for循环从头到尾把上面Wyothoff Game的第n项公式循环一遍,并分别与n,m比较。
代码:
int a,b,t;
//判断容易,要输出的有点复杂
double p=((sqrt(5.0))+1.0)/2;//威佐夫博奕公式
while(~scanf("%d%d",&a,&b),a,b)
{
if(a>b)
swap(a,b);
int s=b-a;
int k=floor(s*p);
if(k==a)//判断是否是必胜局面
printf("0\n");
else
{
printf("1\n");
if(a-k==b-(k+s)&&a>=k)//从两堆中拿走相同数量的石子 注意两个条件都要写,不然WA
printf("%d %d\n",k,k+s);
for(int i=0; ;i++)//从一堆中拿
{
int temp=i*p;
if(temp>=b)
break;
if(b>temp+i&&a==temp)//从b中拿石子,且拿完b比a大
printf("%d %d\n",temp,temp+i);
else if(a==temp+i)//从b中拿石子,但拿完b比a小
printf("%d %d\n",temp,temp+i);
else if(b==temp+i&&a>temp)//从a中拿,拿完肯定a还是小于b
printf("%d %d\n",temp,temp+i);
}
}
}
NYOJ 585 取石子(六)
有n堆石子,每堆石子都有任意个,A和B轮流从取任意堆里取一定的石子,每次只能从一堆里至少取一个,A先取,先取完者胜,问谁胜?
此题为尼姆博弈,
结论:将n个数异或一遍,若不为0,则A胜,否则B胜。
01.#include
02.int main()
03.{
04.int N;
05.scanf("%d",&N);
06.while(N--)
07.{
08.int n,s=0,x;
09.scanf("%d",&n);
10.while(n--)
11.scanf("%d",&x),s^=x;
12.if(s)
13.printf("PIAOYI\n");
14.else
15.printf("HRDV\n");
16.}
NYOJ888取石子(九)
题意同取石子(六),不过先取完者败,问谁胜?
尼姆博弈的变形,统计一下所有数一下大于一的个数,并将所有数字异或一遍,若大于一的个数为0&&异或之后为0||大于一的个数大于0&&异或之后不为零,则A(Yougth)胜,否则B(Hrdv)胜,
代码:
01.#include
02.int main()
03.{
04.int nn;
05.scanf("%d",&nn);
06.while(nn--)
07.{
08.int n,x=0,s=0,z;
09.scanf("%d",&n);
10.while(n--)
11.{
12.scanf("%d",&z);
13.if(z>1) s++;
14.x^=z;
15.}
16.if(x&&s||!x&&!s)
17.printf("Yougth\n");
18.else
19.printf("Hrdv\n");
20.}
21.}
NYOJ135取石子(二)
题意同上上取石子(六),不过限定了每堆石子最多可以取的石子数(最少为一),问A是胜还是败?(第一行是一个整数T表示测试数据的组数(T<100)每组测试数据的第一行是一个整数N(1
01.#include
02.#include
03.int main()
04.{
05.int nn;
06.scanf("%d",&nn);
07.while(nn--)
08.{
09.int n,a,b,c=0;
10.scanf("%d",&n);
11.while(n--)
12.{
13.scanf("%d%d",&a,&b);
14.a=a%(b+1);
15.c^=a;
16.}
17.if(c)
18.puts("Win");
19.else
20.puts("Lose");
21.}
22.}
NYOJ358取石子(五)
有一堆石子,A和B轮流从中取一定的石子,但规定:第一次不能取完,至少一个;从第二次开始,每个人取的石子数至少为1,至多为对手刚取的石子数的两倍。A先取,问A是否会胜?斐波那契博弈
结论:若其对应的石子数目刚好是斐波那契数,则A必败,否则A必胜。
代码:
01.#include
02.long long a[110]={0,1};
03.int main()
04.{
05.long long i,n;
06.for(i=2;i<100;i++)
07.a[i]=a[i-1]+a[i-2];
08.while(scanf("%lld",&n)!=EOF)
09.{
10.int f=0;
11.for(i=1;i<100;i++)
12.if(a[i]==n)
13.{f=1;break;}
14.if(f)
15.printf("No\n");
16.else
17.printf("Yes\n");
18.}
19.}
附4种常见博弈论原理简单介绍:
巴什博弈:
只有一堆n个物品,两个人轮流从这堆物品中取物,规定每次至少取一个,最多取m个。最后取光者得胜。
显然,如果n=m+1,那么由于一次最多只能取m个,所以,无论先取者拿走多少个,后取者都能够一次拿走剩余的物品,后者取胜。因此我们发现了如何取胜的法则:如果n=(m+1)r+s,(r为任意自然数,s≤m),那么先取者要拿走s个物品,如果后取者拿走k(≤m)个,那么先取者再拿走m+1-k个,结果剩下(m+1)(r-1)个,以后保持这样的取法,那么先取者 肯定获胜。总之,要保持给对手留下(m+1)的倍数,就能最后获胜。
威佐夫博弈(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。两个人如果都采用正确操作,那么面对非奇异局势,先拿者必胜;反之,则后拿者取胜。
那么任给一个局势(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,b = aj + j + 1,若都不是,那么就不是奇异局势。然后再按照上述法则进行,一定会遇到奇异局势。
尼姆博奕:
有三堆各若干个物品,两个人轮流从某一堆取任意多的物品,规定每次至少取一个,多者不限,最后取光者得胜。
这种情况最有意思,它与二进制有密切关系,我们用(a,b,c)表示某种局势,首先(0,0,0)显然是奇异局势,无论谁面对奇异局势,都必然失败。第二种奇异局势是(0,n,n),只要与对手拿走一样多的物品,最后都将导致(0,0,0)。仔细分析一下,(1,2,3)也是奇异局势,无论自己如何拿,接下来对手都可以将其变为(0,n,n)的情形。
计算机算法里面有一种叫做按位模2加,也叫做异或的运算,我们用符号(+)表示这种运算,先看(1,2,3)的按位模2加的结果:
1 =二进制01
2 =二进制10
3 =二进制11 (+)
———————
0 =二进制00 (注意不进位)
对于奇异局势(0,n,n)也一样,结果也是0。
任何奇异局势(a,b,c)都有a(+)b(+)c =0。
注意到异或运算的交换律和结合律,即a(+)a=0,:
a(+)b(+)(a(+)b)=(a(+)a)(+)(b(+)b)=0(+)0=0。
所以从一个非奇异局势向一个奇异局势转换的方式可以是:
1)使 a = c(+)b
2)使 b = a(+)c
3)使 c = a(+)
斐波那契博弈:
有一堆个数为n(n>=2)的石子,游戏双方轮流取石子,规则如下:
1)先手不能在第一次把所有的石子取完,至少取1颗;
2)之后每次可以取的石子数至少为1,至多为对手刚取的石子数的2倍。
约定取走最后一个石子的人为赢家,求必败态。
结论:当n为Fibonacci数的时候,必败。
f[i]:1,2,3,5,8,13,21,34,55,89……