[51Nod 1185 威佐夫游戏 V2]Wythoff Game+乘法模拟
题目链接:[51Nod 1185 威佐夫游戏 V2]
题意描述:有2堆石子。A B两个人轮流拿,A先拿。每次可以从一堆中取任意个或从2堆中取相同数量的石子,但不可不取。拿到最后1颗石子的人获胜。假设A B都非常聪明,拿石子的过程中不会出现失误。给出2堆石子的数量,问最后谁能赢得比赛。
例如:2堆石子分别为3颗和5颗。那么不论A怎样拿,B都有对应的方法拿到最后1颗。
注意:这个题目每堆石子的数目是在
[1,1018]
范围内。
解题思路:根据Wythoff 博弈定理:当且仅当
a=⌊k∗5√+12⌋,b=a+k
时,先手失败;否则后手失败。由于题目给定的石子数目很大,所以不能用浮点数直接进行运算。
[tips:5√+12=1.6180339887498948482045868343656...]
,这里用到了乘法模拟的方法,来增大精度。
这题还有一个Java大数的做法, 请参考另外一篇博客《 [hdu 5973 Game of Taking Stones] Wythoff Game+大数运算 》
#include <map>
#include <set>
#include <queue>
#include <cmath>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#define FIN freopen("input.txt","r",stdin)
#define FOUT freopen("output.txt","w",stdout)
#define fst first
#define snd second
#define rep(i, f, t) for(int i = (f); i <= (t); i++)
//typedef __int64 LL;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
const ULL MOD = 1000000000; /// 1e9
int T;
ULL A, B;
ULL X[] = {618033988, 749894848, 204586834};
ULL Y[2], Z[4];
bool check(ULL x, ULL y) {
if(x > y) swap(x, y);
/// 转化为1e9进制数的模拟乘法,计算k*618033988749894848204586834,结果保存在Z[]中
ULL k = y - x, temp = 0;
Y[0] = k / MOD, Y[1] = k % MOD;
Z[3] = (temp = X[2] * Y[1] + temp) % MOD, temp /= MOD;
Z[2] = (temp = X[1] * Y[1] + X[2] * Y[0] + temp) % MOD, temp /= MOD;
Z[1] = (temp = X[0] * Y[1] + X[1] * Y[0] + temp) % MOD, temp /= MOD;
Z[0] = (temp = X[0] * Y[0] + temp) % MOD, temp /= MOD;
return x == temp * MOD + k + Z[0];
}
int main() {
#ifndef ONLINE_JUDGE
FIN;
#endif // ONLINE_JUDGE
scanf("%d", &T);
while(T --) {
cin >> A >> B;
puts(check(A, B) ? "B" : "A");
}
return 0;
}
补充一个Java代码:
import java.math.BigDecimal;
import java.util.Scanner;
public class Main {
static BigDecimal sqrt_five, G;
static BigDecimal two, five;
public static void init() {
two = new BigDecimal(2);
five = new BigDecimal(5);
BigDecimal low, high, mid;
low = new BigDecimal(0);
high = new BigDecimal(5);
mid = new BigDecimal(0);
for(int i = 0; i < 100; i++) {
mid = low.add(high).divide(two);
if(mid.multiply(mid).compareTo(five) > 0) high = mid;
else low = mid;
}
sqrt_five = mid;
// G = (sqrt(5) + 1) / 2;
G = sqrt_five.add(BigDecimal.ONE).divide(two);
}
public static void main(String[] argv) {
BigDecimal a, b, c, left, right;
init();
int T;
Scanner input = new Scanner(System.in);
T = input.nextInt();
while ((T --) != 0) {
a = input.nextBigDecimal();
b = input.nextBigDecimal();
if (a.compareTo(b) > 0) {
c = a;
a = b;
b = c;
}
left = b.subtract(a).multiply(G);
right = a;
// 向下取整。统一精度。
left = left.setScale(0, BigDecimal.ROUND_DOWN);
right = right.setScale(0, BigDecimal.ROUND_DOWN);
if(left.compareTo(right) == 0) System.out.println("B");
else System.out.println("A");
}
return;
}
}