Fibonacci博弈原理与证明

简介

Fibonacci博弈的定义是:

有一堆物品,两人轮流取,先手第一次可以取任意个但不能全部取完。之后每次取的数量必须大于等于 \(1\) 且小于等于上次取的数量的两倍,最后把物品全部取完者胜利

现在给出初始的物品数 \(n\) ,判断先手是否有必胜策略

推理

\(f(i)\) 表示斐波那契数列的第 \(i\)

证明:当 \(n\) 为大于等于 \(2\) 的斐波那契数时先手必败

  • \(n=2\) 时,显然先手必败

  • \(n\leq f(k)\) 且为斐波那契数时,先手必败

    那么 \(n=f(k+1)\) 时,先手第一次不能拿大于等于 \(f(k-1)\) 个物品,如果这样,那么第一次拿完后剩余的物品数为:

    \[n^{'}\leq f(k+1)-f(k-1)=f(k)=f(k-1)+f(k-2)\leq 2f(k-1) \]

    那么后手在第二次可以直接取得胜利,因此先手第一次拿的物品必须小于 \(f(k-1)\)

    \(f(k-1)\) 看作一个子游戏,由假设可得后手可以恰好把 \(f(k-1)\) 取完,那么现在游戏只剩 \(f(k)\) 个物品,由假设得先手必败

综上,\(n\) 为斐波那契数时先手必败

接下来我们证明: \(n\) 不为斐波那契数时先手必胜

首先引入 齐肯多夫定理 :任何整数都可以被分解成若干个不连续的斐波那契数之和

证明:

  • \(n=1,2,3\) 时,\(f(2)=1,f(3)=2,f(4)=3\) 命题成立

  • \(n\leq k\) 时命题都成立

    那么 \(n=k+1\) 时,若 \(n\) 为斐波那契数,命题成立

    \(n\) 不是斐波那契数时,设 \(f(m)<n<f(m+1),n^{'}=n-f(m)\) ,则有:

    \[n^{'}<f(m+1)-f(m)=f(m-1)<n \]

    所以 \(n^{'}\) 可以用不连续的斐波那契数之和表示,设表示中最大的一项斐波那契数为 \(f(a)\) 则有 \(f(a)\leq n^{'}<f(m-1),a<m-1\) ,所以 \(a\)\(m\) 不是连续整数

    \(n=f(m)+n^{'}=f(m)+f(a)+\cdots\) 也符合条件,命题成立

\(n\) 不为斐波那契数时,设 \(n=f(a_1)+f(a_2)+\cdots+f(a_p)\) ,数列单调递减且不为连续整数,那么先手取 \(f(a_p)\) 时,由于 \(a_p\leq a_{p-1}-2\) ,所以有:

\[2f(a_p)\leq 2f(a_{p-1}-2)\leq f(a_{p-1}-2)+f(a_{p-1}-1)\leq f(a_{p-1}) \]

当且仅当 \(a_p=1,a_{p-1}=3\) 时取等号

此时若数列每项差值都为 \(2\) ,那么数列的形式即为:

\[1,2,5,13,\cdots \]

易证 \(n=f(1)+f(3)+\cdots+f(2x-1)=f(2x)\) 与假设不为斐波那契数矛盾

那么必然存在 \(k\) 使 \(a_k-a_{k+1}\geq 3\)\(\forall i>k,a_i-a_{i+1}=2\) ,可以将 \(f(a_{k+1})+f(a_{k+2})+\cdots+f(a_p)\) 变为 \(f(a_{k+1}+1)\)\((a_{k+1}+1)+2\leq a_k\) 满足不连续整数的限制,\(n\) 可以写作:

\[n=f(a_1)+f(a_2)+\cdots+f(a_k)+f(a_{k+1}+1) \]

所以可以把这种情况转化为下面讨论的不取等号的情况

不取等号时,后手只能取 \(f(a_{p-1})\) 的一部分,将视角转移到后手上,这就相当于一个 \(n=f(a_{p-1})\) 的子游戏,而按照之前证明的,后手作为子游戏的先手必败,那么先手恰好可以拿完 \(f(a_{p-1})\) ,以此类推,每一项斐波那契数都会由先手拿完,先手一定胜利

代码

HDU 2561

#include<bits/stdc++.h>
using namespace std;

int f[200 + 5];
map<int, bool> mp;

int main()
{
    f[1] = f[2] = 1;
    for(int i = 3; i <= 50; i++) {
        f[i] = f[i - 1] + f[i - 2];
        mp[f[i]] = true;
    }
    int x;
    while(scanf("%d", &x) != EOF && x)
        printf("%s\n", mp[x] ? "Second win" : "First win");
    return 0;
}
posted @ 2022-02-15 11:19  f(k(t))  阅读(265)  评论(0编辑  收藏  举报