【博弈论】威佐夫博弈

110 阅读1分钟

「这是我参与11月更文挑战的第21天,活动详情查看:2021最后一次更文挑战」。

简介

威佐夫博弈(Wythoff GameWythoff\ Game):有两堆物品个数是nnmm,甲乙两个人轮流从一堆选取kk个物品或两堆中选取各选取kk个物品,最后取完物品的人获胜。

思路

两堆物品数用(n,m)(n,m)表示,满足nmn\le m,如果n>mn>m则交换nnmm

现考虑各种状态,发现先手必输态如下:

n=0n=0时,
(0,0)(0,0)没有物品甲必败,
(0,m0)(0,m\ne 0)有物品甲必胜。

n=1n=1时,
(1,2)(1,2)会变成乙选(0,1)(0,1),(0,2)(0,2)(1,1)(1,1),甲必败,
(1,m2)(1,m\ne 2)有物品甲必胜利。

n=2n=2时,
(2,m)(2,m)变成乙选(0,2)(0,2)(1,2)(1,2),甲必胜。

n=3n=3时,
(3,5)(3,5)甲必败,
(3,m5)(3,m\ne 5)甲必胜。

进一步推算可以发现所有的先手必败局为(0,0)(0,0),(1,2)(1,2),(3,5)(3,5),(4,7)(4,7),(6,10)(6,10)\dots
这种先手必败局叫做奇异局势,可以发现第kk项的nn是前k1k-1项中未出现的最小正整数ii,而m=i+km=i+k
每个人要想获胜就是尽量使对方变成奇异局势。

根据相关资料可以得知第kk项奇异局势的nk=k((5)+1)2,mk=nk+kn_k=\frac{k(\sqrt(5)+1)}{2},m_k=n_k+k

bool Wythoff(int n,int m){//判断先手且为n,m时是否取胜 
    if(n>m) return Wythoff(m,n);//保证n<=m 
    int k=m-n;//计算出项数k 
    int _n=k*(sqrt(5)+1)/2.0;//计算出必败时的n 
    return n!=_n;//判断是否必败 
}

例题

hdu1527

题目描述

有两堆石子,数量任意,可以不同。游戏开始由两个人轮流取石子。游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子;二是可以在两堆中同时取走相同数量的石子。最后把石子全部取完者为胜者。现在给出初始的两堆石子的数目,如果轮到你先取,假设双方都采取最好的策略,问最后你是胜者还是败者。

输入

输入包含若干行,表示若干种石子的初始情况,其中每一行包含两个非负整数a和b,表示两堆石子的数目,a和b都不大于1,000,000,000。

输出

输出对应也有若干行,每行包含一个数字1或0,如果最后你是胜者,则为1,反之,则为0。

样例输入

2 1 8 4 4 7

样例输出

0 1 0

参考代码

#include <iostream>
#include <cmath>
using namespace std;
bool Wythoff(int n,int m){//判断先手且为n,m时是否取胜 
    if(n>m) return Wythoff(m,n);//保证n<=m 
    int k=m-n;//计算出项数k 
    int _n=k*(sqrt(5)+1)/2.0;//计算出必败时的n 
    return n!=_n;//判断是否必败 
}
int main(){
    int n,m;
    while(cin>>n>>m){
        cout<<Wythoff(n,m)<<endl;
    }
    return 0;
}