1185 威佐夫游戏 V2 嗯,现在有了初步理解

基准时间限制:1 秒 空间限制:131072 KB 分值: 0  难度:基础题
 收藏
 关注

有2堆石子。A B两个人轮流拿,A先拿。每次可以从一堆中取任意个或从2堆中取相同数量的石子,但不可不取。拿到最后1颗石子的人获胜。假设A B都非常聪明,拿石子的过程中不会出现失误。给出2堆石子的数量,问最后谁能赢得比赛。
例如:2堆石子分别为3颗和5颗。那么不论A怎样拿,B都有对应的方法拿到最后1颗。

Input
第1行:一个数T,表示后面用作输入测试的数的数量。(1 <= T <= 10000)
第2 - T + 1行:每行2个数分别是2堆石子的数量,中间用空格分隔。(1 <= N <= 10^18)
Output
共T行,如果A获胜输出A,如果B获胜输出B。
Input示例
3
3 5
3 4
1 9
Output示例
B
A
A

首先,这个题目我本身就不太理解= =
第一堆为3 第二堆为5,那A先拿第一堆的3个,B拿第二堆的1个,A再拿四个不就是A赢了?

不过后来查了几个博客,发现这个是典型的威佐夫博弈问题。

so,只能从头去理解了。

这个博客总结了威佐夫博弈问题,可以参考

点击打开链接

嗯,现在有了初步理解

现在一致先手必输的情况为:

(0,0)

(1,2)

(3,5)

(4,7)

(5,9) 

    ……

因此可以得出先手输掉的局面,第一堆和第二堆石子数量的差值为0,1,2,3,4……,为递增数列,且第一堆石子数量为前面

没有出现的最小自然数。(第一堆的数字必须是两者之间小的那一个,如果不是则需要交换位置)

并且奇妙的是,再找规律的话我们会发现,第一个值 = 差值 * 1.618 

而1.618 = (sqrt(5)+ 1) /  2 。

大家都知道0.618是黄金分割率。而威佐夫博弈正好是1.618,这就是博弈的奇妙之处!

因此这道题目前来说大概就明了了。

如果所输入的石子数,两者之间的差值乘以黄金分割率不等于第一个石子堆个数,则A赢,反之,则B赢。

但是

事情远远没有这么简单

这道题有精度要求,所以不能直接简单去乘1.618

题目要求:石子数最多有18位,所以要把它分开= =

第2 - T + 1行:每行2个数分别是2堆石子的数量,中间用空格分隔。(1 <= N <= 10^18)

具体实现

上面提到,由于是大数,直接乘以(sqrt(5)+1)/2会有精度问题。

那么我们就来模拟一下两数之差和(sqrt(5)+1)/2相乘的过程,

它等价为:(b-a)*黄金分割比例+(b-a) . 

我们用tmp数组存储黄金分割比例的小数点后1~9位、10~18位、19~27位。

用l存储(b-a)的高9位,r存储(b-a)的低9位。

则两数相乘有:

                         tmp[0]       tmp[1]      tmp[2]

                   *                         l                r       

                        r*tmp[0]    r*tmp[1]     r*tmp[2]  

    +  l*tmp[0]    l*tmp[1]     l*tmp[2]

以上是模拟乘法的过程,只需要把每一位对应的数相加再加上低位,最后加上(b-a)即可。

代码实现:

#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std; 
long long mod=1e9;
int main()
{
	int t;
	cin>>t;
	long long ratio[3]={618033988,749894848,204586834};
	while(t--)
	{
		long long num1,num2;
		cin>>num1>>num2;
		if(num1>num2)
		swap(num1,num2);
	    long long cha=num2-num1;//差 
		long long r=cha%mod;//低九位 
		long long l=cha/mod;//高九位 
		//开始模拟 
		long long sum=r*ratio[2];//最低位
		sum=l*ratio[2]+r*ratio[1]+sum/mod;//倒数第二位 +最低位 
		sum=l*ratio[1]+r*ratio[0]+sum/mod;//倒数第三位 +倒数第二位 +最低位
		sum=cha+l*ratio[0]+sum/mod;//同理,由于ratio其实是小数点后位数,所以除了3次1e9 
		if(sum==num1) 
		cout<<"B"<<endl; 
		else
		cout<<"A"<<endl;
	} 
	return 0;
}

参考网址:

https://blog.csdn.net/qq_34374664/article/details/52814983

https://blog.csdn.net/qaz135135135/article/details/52184128?locationNum=9&fps=1



猜你喜欢

转载自blog.csdn.net/qq_40421671/article/details/80986761