博弈(四) 斐波那契博弈(Fibonacci Nim)

斐波那契博弈(Fibonacci Nim)

有一堆n个物品,两个人轮流拿,拿到最后一个的人获胜。拿的规则如下:
1.每个人最少拿一个,第一次先手不能一次全拿完。
2.每次最多可拿对手上一次拿的两倍。
结论:
先手胜利当且仅当物品数n不为Fibonacci数。
证明:
首先需要了解Fibonacci的性质:(2, 3, 5, 8, 13, 21, 34, 55··· ···)
1.f(n) = f(n-2) + f(n-1)
2.2f(n) > f(n+1)
3.4f(n) < 3f(n+1)
运用数学归纳法证明:
1.当n=1的时候,f(n) = 2,此时先手必输,满足结论。
2.当n=2的时候,f()n = 3,此时先手必输,满足结论。
3.假设当n=k的时候满足结论,讨论当n=k+1的情况:
当n=k+1时,我们可以将这一堆分成f(k)和f(k-1)两堆。由性质2我们可以知道f(k+1) = f(k-1)+f(k) < f(k-1) + 2f(k-1) = 3f(k-1),即f(k+1) < 3f(k-1)。两个人每次都是最优策略,先手不可能一次拿完f(k-1)这一堆,因为如果这样剩下的就小于2f(k-1),后手就可以一次拿完获得胜利。所以先手拿的肯定小于f(k-1),由之前我们的假设f(k-1)是满足结论的,即无论先手怎么拿,后手都可以拿完f(k-1)的最后一个,这样就剩下f(k)堆了。下面判断先手能不能一次把f(k)堆拿完:在f(k-1)堆中,先手最多拿不能超过1/3f(k-1),后手最多拿2/3f(k-1),之后先手最多拿4/3f(k-1),由性质3可得4/3f(k-1) < f(k),即按照最多的拿之后先手都不可能一下把f(k)堆拿完。所以之后又转入和f(k-1)堆同样的情况,后手总能拿完f(k)堆最后一个。所以先手必输。满足结论。
当n不是Fibonacci数的时候,由齐肯多夫定理可知:任何正整数都可以表示成若干个不连续的斐波那契数(不包括第一个斐波那契数)之和
然后对于任意正整数n进行分解,例如54 = 34 + 13 + 5 + 2.
n = f(an) + f(an-1) + ··· ··· + f(a3) + f(a2) + f(a1)
先手首先拿走f(a1)这一堆。因为不是连续的Fibonacci数, 即a2 > a1 + 1。所以f(a2)肯定大于2f(a1),即后手不可能一次把f(a2)堆拿完。然后对于f(a2)堆满足结论,后手必输,之后对于每一堆都满足相同结论,即先手总能拿完最后一个。先手必赢。
例题:
HDU - 2516
代码:

#include<bits/stdc++.h>

using namespace std;
const int maxn = 100 + 5;
long long f[maxn];
int n;
void getFibonacci() {
    f[0] = 2;
    f[1] = 3;
    for(int i = 2; i <= maxn; i++) {
        f[i] = f[i-2] + f[i-1];
        if(f[i] > 2147483648) return;
    }
}

int main() {
    getFibonacci();
    while(cin >> n, n) {
        int flag = 0;
        for(int i = 0; i < maxn; i++) {
            if(f[i] == n) break;
            else if(f[i] > n) flag = 1;
        }
        if(flag) cout << "First win" << endl;
        else cout << "Second win" << endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/l1832876815/article/details/81136041