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