nyoj的取石子有好多道,除了两道难度为6的,剩下的在这儿简单总结一下结论。
有一堆石子共有n个,A和B轮流取,A先,每次最少取1个,最多取m个,先取完者胜,A,B足够聪明,问谁先胜?
比较简单的巴什博弈,若n%(m+1)!=0,A胜,否则B胜。
n个石子摆成一圈,A和B轮流取,每次可以从中取一个或相邻两个,先取完者胜,A先取,问谁胜?
若n==1||n==2 则A胜,否则B胜。
两堆石子分别n,m(n>=m)个,A和B轮流取,有两种取法,一是在任意的一堆中取走任意多的石子,最少为一;二是在两堆中同时取走相同数量的石子。A先取,先取完者胜,问A是否胜?(胜输出1,负为0)
著名的威佐夫博奕,题解链接:威佐夫博弈,
结论:若(n-m)*(sqrt(5.0)+1.0)/2.0!=m ,则A胜,否则负。
01.
#include<stdio.h>
02.
#include<math.h>
03.
#include<iostream>
04.
#include<algorithm>
05.
using
namespace
std;
06.
int
main()
07.
{
08.
int
a,b;
09.
while
(
scanf
(
"%d%d"
,&a,&b)!=EOF)
10.
{
11.
if
(a<b)
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.
}
题意同上取石子(四),不过现在要求前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<stdio.h>
02.
#include<math.h>
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.
}
题意同上上取石子(四),依然为威佐夫博弈,但是如果A胜,要你输出你第一次怎么取子,(如果在任意的一堆中取走石子能胜同时在两堆中同时取走相同数量的石子也能胜,先输出取走相同数量的石子的情况,假如取一堆的有多种情况,先输出从石子多的一堆中取的情况,且要求输出结果保证第二个值不小于第一个值。)
首先判断若n==0,则直接输出(0,0);
else for循环从头到尾把上面Wyothoff Game的第n项公式循环一遍,并分别与n,m比较。
核心代码:
01.
for
(i=1; i<=n; i++)
02.
{
03.
int
c=i*(
sqrt
(5.0)+1.0)/2.0;
04.
int
d=c+i;
05.
if
(c>n)
break
;
06.
if
(c==m&&d<n)
07.
printf
(
"%d %d\n"
,m,d);
08.
else
if
(d==m&&c<n)
09.
printf
(
"%d %d\n"
,c,m);
10.
else
if
(d==n&&c<m)
11.
printf
(
"%d %d\n"
,c,n);
12.
}
有n堆石子,每堆石子都有任意个,A和B轮流从取任意堆里取一定的石子,每次只能从一堆里至少取一个,A先取,先取完者胜,问谁胜?
此题为尼姆博弈,题解链接:尼姆博弈;
结论:将n个数异或一遍,若不为0,则A胜,否则B胜。
01.
#include<stdio.h>
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.
}
题意同取石子(六),不过先取完者败,问谁胜?
尼姆博弈的变种,统计一下所有数一下大于一的个数,并将所有数字异或一遍,若大于一的个数为0&&异或之后为0||大于一的个数大于0&&异或之后不为零,则A(Yougth)胜,否则B(Hrdv)胜,
代码:
01.
#include<stdio.h>
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.
}
题意同上上取石子(六),不过限定了每堆石子最多可以取的石子数(最少为一),问A是胜还是败?(第一行是一个整数T表示测试数据的组数(T<100)每组测试数据的第一行是一个整数N(1<N<100),表示共有N堆石子,随后的N行每行表示一堆石子,这N行中每行有两个数整数m,n表示该堆石子共有m个石子,该堆石子每次最多取n个。
(0<=m,n<=2^31))
01.
#include<stdio.h>
02.
#include<string.h>
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.
}
有一堆石子,A和B轮流从中取一定的石子,但规定:第一次不能取完,至少一个;从第二次开始,每个人取的石子数至少为1,至多为对手刚取的石子数的两倍。A先取,问A是否会胜?
具体过程:斐波那契博弈
结论:若其对应的石子数目刚好是斐波那契数,则A必败,否则A必胜。
代码:
01.
#include<stdio.h>
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.
}