51nod 1185 威佐夫游戏 V2 (用乘法模拟解决大数精度问题)

传送门:51nod 1185


Input示例
3
3 5
3 4
1 9
Output示例
B
A
A


题目大意

中文题不解释。需要注意的是题目给的数据比较大 1<= N <=10^18,直接用威佐夫博弈做会产生精度问题,什么是精度问题呢?比如一个大数乘以黄金分割比例0.618…的小数点后9位和小数点后27位得到的结果有可能是不同的,当然乘以小数点后面的数越多结果越精确。



思路

一般的威佐夫博弈的做法是,两堆石子数分别为a,b,其中a



具体实现

上面提到,由于是大数,直接乘以(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)即可。



注:

至于黄金分割比例的27位小数是怎么获取的,这个貌似只能百度吧……有什么好的方法还欢迎留言告知,多谢~


之前有一点一直没想明白,我们要乘以的是黄金分割比0.618…而tmp数组存储的缺是一个27位的大整数。代码中结果却没有明显的再除以10^27的式子。这是怎么回事呢?其实我们需要的是结果的整数部分,最低9位的结果只会对次低9位的结果产生影响,依此类推。所以下面代码在计算结果的时候都是当前位的值加上 次低9位/mod 的值。一共除以了3次,也就相当于让结果共除以了10^27。



代码:

#include
#include
typedef long long LL;
int main()
{
	int i,t;
	LL a,b,c,d,l,r,sum;
	LL mod=1e9;
	LL tmp[3]={618033988,749894848,204586834}; //存储黄金分割比例的1~27位 
	scanf("%d",&t);
	while(t--)
	{
		scanf("%lld%lld",&a,&b);
		if(a>b)
		{ //保证b>=a 
			c=a;
			a=b;
			b=c;
		}
		d=b-a;   //d存储两数之差 
		l=d/mod; //l存储差的高9位 
		r=d%mod; //r存储差的低9位
		//以下为模拟乘法的过程 
		sum=r*tmp[2];
		sum=r*tmp[1]+l*tmp[2]+sum/mod;
		sum=r*tmp[0]+l*tmp[1]+sum/mod;
		sum=d+l*tmp[0]+sum/mod;  //d+d*黄金分割比例 
		if(sum!=a) printf("A\n");
		else printf("B\n");
	}	
	return 0;
}

你可能感兴趣的:(C语言,算法,博弈论)