博弈(二) 威佐夫博弈(Wythoff's game)

威佐夫博弈(Wythoff’s Game)

有两堆物品分别为(an, bn)。两个人轮流取,至少取一个,有两种取法,取到最后一个者胜利。
1.从任意一堆中取任意个 > 1。
2.从两堆中取同样多个。
结论:
对于任意的局势(a, b)(a < b),必败点为(b-a) * (sqrt(5)+1)/2 = a.
证明:
第一个必败点为(0, 0),即谁面对当前局势必输。
第二个必败点为(1, 2),无论先手怎么取,后手都可以通过一步将局势转为(0, 0)。
第三个必败点为(3, 5),后手总可以通过一步转为(0, 0)或者(1, 2)。
接下来的必败点为(4, 7), (6, 10), (8, 13)··· ···
总结规律:
1.对于任何一个必败点局势(a, b),a都为之前未出现的最小整数.
2.必败点的差值满足等差数列0,1,2,3,4,5··· ···
3.对于每个必败点,a = (int)(b-a)*(sqrt(5) + 1)/2。((sqrt(5) + 1)/2为1.618, 0.618为黄金分割)
性质:
任何非必败点都可以通过一步适当操作转化为必败点。
1.a=b,同时拿走a,剩下(0, 0)
2.a<b,两种方法:
1)同时拿,差不变。所以同时拿走a - (b-a)*1.618个后转为必败点。(前提a > (b-a)*1.618)
2)只拿一个的情况。遍历0-b枚举差值,找a-i和b或者b-i和a满足条件的局势即可。
例题:
HDU - 1527
直接判断a = (int)(b-a)*(sqrt(5) + 1)/2即可。
代码:

#include

using namespace std;
int a, b;

int main() {
    while(cin >> a >> b) {
        int sum = a + b;
        a = min(a, b);
        b = sum - a;
        if((int)(double (b-a) * (sqrt(5)+1) / 2) == a) cout << 0 << endl;
        else cout << 1 << endl;
    }
    return 0;
}

你可能感兴趣的:(博弈,ACM)