HDU 1527 (POJ 1067) 取石子游戏 Wythoff Game

46 篇文章 0 订阅
29 篇文章 0 订阅

题目大意:
就是Wythoff Game的原型, 每次可以拿其中一堆的任意数量或者两堆同时拿一样的任意数量, 拿走最后一颗石子的人胜

大致思路:
首先可以参考这篇Wythoff Game的论文:
Wythoff Game
其中提到了一个Beatty Theorem(贝蒂定理):
如果 a b都是无理数, 且 1a+1b=1 , 那么 {a,2a,3a...} {b,2b,3b...} 两个集合之间没有相同元素且他们并组成正整数集 ( a 表示向下取整)

另外Wythoff Game当中一个重要的表:

The Sequence of Safe Pairs (A, B)

n012345678910
A013468911121416
B025710131518202326

这里我们是不妨假设了 AB
其中 (A,B) P 点的条件是 A=ϕn,B=A+n 是 第n个 P 点, 当然 Bϕ2n
其中 ϕ 是黄金比例 ϕ=1+52

上表中的一些性质
将第 i 列的A B 记做A[i],B[i]的话, B[i]=A[i]+i , 并且, 如果第 i 列是(A[i],B[i]), 那么第 B[i] 列一定满足 A[B[i]]=A[i]+B[i]
对于每一个 A[k] , A[k] 一定是前面 (A[i],B[i]) 中没有出现过的最小自然数, 并且任何一个自然数都包含在一个且仅有的一个 P 点中(也有文章称为奇异局势), 这里我们只考虑了A[i]B[i]的局势
那么接下来说明任意操作都可以将奇异局势( P 点)变成非奇异局势(N点)
如果 (A[k],B[k]) 是奇异局势( P 点), 如果只改变其中一个数, 那么得到的分量不可能在其他奇异局势中(P点), 因为每个奇异局势中的自然数是唯一的, 如果将两个数同时减少, 由于两个数的差不变, 不可能是其他奇异局势的差, 因为第i个奇异局势的差是 B[i]A[i]=i , 于是从奇异局势( P 点)只能变成非奇异局势(N点)
对于一个非奇异局势( N 点),记为(a,b), 如果 b=a 可以变为 (0,0) 的奇异局势, 那么 a<b 时 如果 a=A[k],b>B[k] , 那么可以变为奇异局势 (A[k],B[k])
如果 a=A[k],b<B[k] , 那么两堆同时拿走 A[k]A[bA[k]] , 很明显由于 B[k]>b>a>A[k] , 这里取走 A[k]A[bA[k]] bA[k]<B[k]A[k]=k , 所以肯定是可取的, 那么这样就变成了 (A[bA[k]],bA[k]+A[bA[k]]) , 也就是 (A[bA[k]],A[bA[k]]+(bA[k])) , B[bA[k]]=A[bA[k]]+(bA[k]) 所以变成了奇异局势( P 点)
如果a>A[k],b=B[k]=A[k]+k, 那么从第一队中拿走多余的即可变为 (A[k],B[k)
如果 a<A[k],b=A[k]+k=B[k]
那么当 a=A[j] (j<k) 从第二堆拿走 bB[j] 即可
a=B[j] (j<k) , 从第二堆拿走 bA[j] 即可
以上两种一定包含了所有情况, 因为之前找到的性质: A[i] 是前面没有出现的最小自然数

以上是学习到的所有Wythoff Game相关的东西了, 这里了一下:

接下来就是代码了, 利用黄金分割来判断是否是(A[i], B[i])即可
代码如下:
Result : Accepted Memory : 1576 KB Time : 31 ms

/*
 * Author: Gatevin
 * Created Time:  2015/5/8 10:14:35
 * File Name: Rin_Tohsaka.cpp
 */
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;
#define foreach(e, x) for(__typeof(x.begin()) e = x.begin(); e != x.end(); ++e)
#define SHOW_MEMORY(x) cout<<sizeof(x)/(1024*1024.)<<"MB"<<endl

const double gold_ratio1 = (1 + sqrt(5.)) / 2;
const double gold_ratio2 = (sqrt(5.) - 1) / 2;
int a, b;

int main()
{
    while(scanf("%d %d", &a, &b) != EOF)
    {
        if(a > b) swap(a, b);
        int t = floor(a*gold_ratio2);
        if(floor(t*gold_ratio1) != a)
        {
            if(a + t + 1 == b) puts("0");
            else puts("1");
        }
        else
        {
            if(a + t == b) puts("0");
            else puts("1");
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值