博弈论(bash+nim+wythoff+fibonacci)

第一行国际惯例咕咕咕。

第二行——博弈是很有趣的,只不过我不会而已。

第三行——我现在怀疑是我的智商问题导致我看了很多博客都不懂nim怎么证明的。(为什么他们的证明方法都一样啊呜呜呜)

 

前言:博弈的题应该可以先考虑找找规律?从小的开始推必胜必败态然后数学归纳试试?(自己瞎总结的

后续:我已经不管什么证明不证明了,我现在已经愉快的决定记住结论来解题了。

证明留个坑吧,不一定什么时候补坑的那种。

一、巴什博弈(同余定理)

巴什博弈:只有一堆n个物品,两个人轮流从这堆物品中取物,规定每次至少取一个,最多取m个。最后取光者得胜。
这个问题看起来很深奥,其实理解透了——也挺深奥的(一个蒟蒻如是说)。

证明:

先剖析题意——每次可以取1~m个,那存在如下几种情况:
1. n%(m+1)==0,此时,无论先手取几个,后手都可以取i个使每轮是m+1个,最终先手必输。
2. n%(m+1)!=0,此时先手先取n%(m+1)个,后手取k个,先手再取l个使k+l=m+1,最后一次即为先手取得。

下附几个基础练习题

1.HDU4764 2.HDU2149 3.HDU2188 4.HDU1847 5.HDU2897

由于HDU2897略有变形,下附代码(代码写的丑就将就一下叭):

#include<iostream>
#include<cstdio>

using namespace std;

int main()
{
    int n,p,q;
    while(~scanf("%d%d%d",&n,&p,&q))
    {
        if(n % (p+q) == 0) printf("WIN\n");
        else
        {
            if(n % (p+q) <= p)  printf("LOST\n");
            else if(n % (p+q) > p && n % (p+q) <= q) printf("WIN\n");
            else printf("WIN\n");
        }
    }
    return 0;
}

二、尼姆游戏(异或定理)

尼姆游戏:有n堆石子,双方轮流取任意一堆石子中的任意数量(不能不取),且每个人都采用对其最有利的策略,最后取走石子的人为胜。

尼姆游戏的特点:1.两个人。2.给定了转移状态。3.状态有限。4.轮流进行。5.结束状态明确。

证明

首先我们定义P为先手必胜,N为先手必败。那么可得(对先手而言):

1.所有能导致N状态的为P  2.所有能导致P状态的为N   3.所有P状态为P  4.所有N状态为N

由当前状态只能往P或N两个状态转移。

n堆石子,①若全部为0,则先手必输。②若其中一堆>0,其余为0则先手必胜。③若所有石子相同,当为偶数堆则先手必输,因为无论先手拿多少,后手都可以从另一堆拿相同的个数;当为奇数,先手必胜,因为先手可先移走一堆,后手变先手。④由③,可以推出,当有偶数堆,并且石子的个数可以找到相同的堆,即可分为两部分,不论前手拿多少,后手都可以拿对称堆的相同个数,最终前手没有石子取。⑤根据④,我们可以将所有的成双堆数移除,变为全部为不相同的石子的堆,对于这些石子,不论怎样取,都会变成偶数堆的状态/全为不同状态,对于所有的石子,进行异或的异或和,一定至少有一堆的石子最高位为异或和的最高位的1(有的数量一定为奇数堆),这里先留个坑,我不知道该怎么往下证明了呜呜呜,现在只会用结论。

结论:①若所有数二进制异或为0,则先手必输,否则,则先手必胜。②若先手必胜,则先手的第一步可以为移走第i堆剩异或和异或第i堆石子个数。

下附练习题:1.洛谷P2197(代码以它为例)2.POJ2234 3.HDU2176 4.HDU1850 5.HDU1907

#include <iostream>
#include <cstdio>

using namespace std;

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,a;
        scanf("%d",&n);
        int res = 0;
        for(int i = 0; i < n; ++i)
            scanf("%d",&a),res ^= a;
        if(res == 0) printf("No\n");
        else printf("Yes\n");
    }
    return 0;
}

三、威佐夫博弈(黄金分割)

威佐夫游戏:有两堆各若干个物品,两个人轮流从任一堆取至少一个或同时从两堆中取同样多的物品,规定每次至少取一个,多者不限,最后取光者得胜。

结论:若初始值为x,y,z = abs(x-y),w = (int)(sqrt(5)+1)*z/2。如果,w = min(x,y),那么先手必败,否则先手必胜。

本代码以POJ1067为例

#include <iostream>
#include <cstdio>
#include <cmath>
#define ll long long

using namespace std;

int main()
{
    ll x,y;
    while(~scanf("%lld%lld",&x,&y))
    {
        if(x > y) swap(x,y);
        ll z = y-x;
        ll q = floor((sqrt(5.0)+1.0)*z/2.0);
        if(q == x) printf("0\n");
        else printf("1\n");
    }
    return 0;
}

四、Fibonacci博弈

斐波那契博弈:给定n个石子,A、B两人轮流取石子,A先取,可以任取石子,但第一次不能全取完,而且至少取一个。每次所取石子数必须大于或等于1,且小于等于对手刚取的石子数的两倍。取到最后一个石子的为胜者。

结论:当为Fibonacci数时,为先手必败态。

证明:首先:Zeckendorf定理表示任何正整数都可以表示成若干个不连续的斐波那契数(不包括第一个斐波那契数)之和。

可利用数学归纳法证明该结论。(我再次留坑

题目:HDU2516

#include <iostream>
#include <cstdio>
#include <set>
#define ll long long

using namespace std;

ll f[55];
set<ll>s;

void fibo()
{
    f[0] = 0;
    f[1] = 1;
    s.insert(0);
    s.insert(1);
    for(int i = 2; i < 55; ++i)
        f[i] = f[i-1] + f[i-2],s.insert(f[i]);
}
int main()
{
    ll n;
    fibo();
    while(~scanf("%lld",&n))
    {
        if(n == 0) break;
        if(s.count(n) == 1) printf("Second win\n");
        else printf("First win\n");
    }
    return 0;
}

 

 

PS:四月写成了巴什的博客,太基础简单。今天想起其他博弈,刚好就学习+重写博客了(一篇博客单讲一个巴什未免太缺少内容)。

 

欢迎指出错误qwq

欢迎给我讲解博弈呜呜呜

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值