POJ 1067 && HDU 1527 取石子游戏(博弈论)

Description
有两堆石子,数量任意,可以不同。游戏开始由两个人轮流取石子。游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子;二是可以在两堆中同时取走相同数量的石子。最后把石子全部取完者为胜者。现在给出初始的两堆石子的数目,如果轮到你先取,假设双方都采取最好的策略,问最后你是胜者还是败者。
Input
输入包含若干行,表示若干种石子的初始情况,其中每一行包含两个非负整数a和b,表示两堆石子的数目,a和b都不大于1,000,000,000。
Output
输出对应也有若干行,每行包含一个数字1或0,如果最后你是胜者,则为1,反之,则为0。
Sample Input
2 1
8 4
4 7
Sample Output
0
1
0
Solution
用 (a, b) 表示两堆石子的个数,即游戏中的一个状态
列举了几个状态之后容易发现,必胜态的数目比必败态要多很多,所以我们先手工求出前几个必败态:
(1, 2)、(2, 1)、(3, 5)、(5, 3)、(4, 7)、(7, 4)、(6, 10)、(10, 6)……
首先回顾必胜态和必败态的朴素求法:
定理 0:一个状态是必败态,当且仅当它的所有后继状态都是必胜态;而一个状态是必胜态,只要它的后继状态有一个以上的必败态即可。
容易发现下面的定理:
定理 1:(a,b) 和 (b, a) 的胜负性是相同的(a <> b)。
证明:如果 (a, b) 是必胜态,那么将必胜策略中所有的操作,对第一堆的变为第二堆,对第二堆的变为第一堆,就构成 (b, a) 的必胜策略
定理 2:若 (a, b) 是必败态,则对于所有的 x <> a 和 y <> b,(x, b) 和 (a, y) 是必胜态。
证明:对于 x > a 和 y > b,不管是哪一种情况,总可以从 x 堆或 y 堆中取出一定量的石子使当前状态变为必败态 (a, b),由定理 1,(x, b) 和 (a, y) 为必胜态。
对于 x < a 和 y < b,不管是哪一种情况,如果 (x, b) 或 (a, y) 是必败态的话,由上述可得 (a, b) 是必胜态,矛盾。故 (x, b) 和 (a, y) 均为为必胜态。
定理 3: 若 (a, b) 是必败态,则对于所有的 d > 0,(a + d, b + d) 是必胜态。
证明:与定理 2 类似。
定理 4:在所有的必败态中,每个数字恰巧出现一次。
证明:有了定理1,对于对称的状态我们只需要处理其中一个,而两个数不会相同(相同的状态必然是必胜态),于是我们把每个状态中较小的数字放在前面,每行写一个状态,去掉括号并按照升序排列每行的第一个数,就构成了如下的矩阵:
1 2
3 5
4 7
6 10
……
观察这个矩阵,我们又可以得到新的定理:
定理 5:矩阵中每行第一个数恰巧是前面每一行中没有出现过的最小正整数。
证明:由定理4,矩阵中每个数字恰巧出现一次,而按照这个矩阵的定义,第二列的数总比同行第一列大,第一列又按照升序排列,所以每一行的第一个数正好为前面每一行中没有出现过的最小正整数。
定理6:矩阵第 i 行的第二个数正好为第一个数加上 i
证明:用数学归纳法。
1) 对于第一行显然成立
2) 若对于前 i - 1 行均成立,则所有的 (a[p], a[p] + p) (a[p] 为第 p 行第一个数,p < i) 均为必败态,那么考察第 i 行的状态 (a[i], a[i] + delta)。容易看出 delta >= i,因为如果 delta < i,一定可以通过一次操作变为前面出现过的必败态,那么这个状态就是必胜态。下面由 delta >= i,我们来说明 delta = i。
首先,我们考虑从第一堆中取出 p 个石子,得到状态 (a[i] - p, a[i] + delta),由定理 5,比 a[i] 小的数都在之前出现过,若 a[i] - p 出现在某一行的第一列,由于存在必败态 (a[i] - p, a[i] - p + d) (d < delta),故 (a[i] - p, a[i] + delta) 一定为必胜态(定理 2);若 a[i] - p 出现在某一行的第二列,由于第一列是单增的,因而其对应的第一列数必小于 a[i] + delta,故而也可推出其状态为必胜态。
对于从两堆石子中取出相同数目的情况与之类似,容易看出一定为必胜态。
于是,(a[i], a[i]+delta) 状态的胜负性只与状态(a[i], a[i]+d)(d

#include<iostream>
#include<cmath>
using namespace std;
double p=(sqrt((double)5)+1)/double(2);
int main ()
{
    int a,b,c;
    while(scanf("%d%d",&a,&b)!=EOF)
    {
        c=abs(a-b);
        a=a>b?b:a;
        if(a==(int)(p*c)) printf("0\n");
        else printf("1\n");
    }
    return 0;
}

你可能感兴趣的:(POJ 1067 && HDU 1527 取石子游戏(博弈论))