chelsea0901

威佐夫博弈---黄金分割比

经典例题:

有两堆石子,有两个绝顶聪明的人在玩一个游戏,每次每个人可以从一堆石子中取任意数量但不少于1个的石子,或从两堆中同时取走相同数量的石子,最后一个取完石子的人获胜。

面对博弈题,最重要的找出必败点

(0,0)(1,2)(3,5)(4,7)(6,10)……

通过观察可以发现奇异局势的特点:

  • An是前面n-1对必败点中没有出现过的最小正整数,Bn = An + n

  • 任何一个自然数都包含在一个且仅有的一个局势中。

  • 任何的操作都可以将奇异局势变成非奇异局势。

  • 通过适当的方法可以将非奇异局势变成奇异局势

对于特点1:

打表找规律即可.jpg

对于特点2:

反证法:假设这个自然数v出现了两次,那么v一定不是奇异局势的a(由特点1得:a只能出现一次),所以v是奇异局势的b,而两个局势的b相同,但两个局势中a与b的差值必不同,则这两个局势必然有一个不是奇异局势,说明v肯定不能出现两次啊

对于特点3:

对于一个奇异局势,若取走一堆中的石子,那么两对石子的差值必然改变,必然成为非奇异局势

对于一个奇异局势,若同时取走两堆石子,因为一个差值只能对应一种奇异局势,现在改变了a,b而差值不变,则必不是奇异局势

对于特点4:

所谓适当的方法,可以分为如下几种:

  • 当a = b时,可以将两堆同时取光,剩下(0,0),为奇异局势
  • 当a = a[k],b > b[k]时,只需取走第二堆的b - b[k]个石子即可
  • 当a = a[k],b < b[k]时,两堆需要同时取走a - k(b - a)个石子,k属于正整数。当两堆同时取完后,就剩下(k(b - a), k(b - a) + (b - a))的奇异局势!!!
  • 当a > a[k],b = b[k],只需第一堆取走a - a[k]个石子即可
  • 当a < a[k], b = b[k],须分两种情况:1.a[k] == aj,即a[k]等于在他之前的任意一个奇异局势的a的值,此时,只需从第二堆取走b - bj个石子即可构成(aj,bj)的奇异局势;2.a[k] == bj,即a[k]等于在他之前的任意一个奇异局势的b的值,此时只需从第二堆取出b - aj个石子即可构成(bj, aj)的奇异局势。

而对于奇异局势(a,b)的判断条件:

ak = k * (sqrt(5) + 1) / 2; bk = ak + k;

具体证明利用的是Betty定理,经过一系列证明啊带入啊得到的,在这里我不太会说明白就不证明了,有兴趣的同学可以去传送门看看。

现在举个vj的例题:K - 取石子游戏

我们只需要判断a b的差值乘以1.618然后取整后是否等于a即可!若等于则先手必输,否则先手必胜!

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a,b;
int main()
{
    while(cin>>a>>b)
    {
        if(a > b)
            swap(a, b);//将小的数放在前面
        if((int)((b - a) * (sqrt(5.0) + 1.0) / 2.0) == a)
            cout<<0<<endl;
        else
            cout<<1<<endl;
    }
    return 0;
}

巴什博弈 --- Bash Game

经典例题:

两个顶尖聪明的人在玩游戏,有n个石子,每人可以随便拿1-m个石子,不能拿的人为败者,问谁会胜利

我们分情况讨论一下下:

  • 当n <= m时,先手必胜
  • 当n == m + 1时,后手必胜
  • 当n == k *(m + 1)时,先手取i个,后手都可以将剩下的m + 1 - i个取走,维持下去,所以后手必胜
  • 当n == k *(m + 1) + x时,先手可以先取走那x个,局势就变成上面的那样,所以先手必胜

结论:

if(n % (m + 1) == 0)
    先手必败
else
    先手必胜

变式:

1、 总共n张牌;
2、 双方轮流抓牌;
3、 每人每次抓牌的个数只能是2的幂次(即:1,2,4,8,16…)
4、 抓完牌,胜负结果也出来了:最后抓完牌的人为胜者;
假设Kiki和Cici都是足够聪明(其实不用假设,哪有不聪明的学生~),并且每次都是Kiki先抓牌,请问谁能赢呢?

思路:

打表找规律能发现:如果是3的倍数,则先手必胜,否则必败

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n;
    cin>>n;
    if(n % 3)
        cout<<"Kiki\n";
    else
        cout<<"Cici\n";
    return 0;
}

尼姆博弈

先从最简单的模型说起:有三堆若干个物品,每次至少拿一个,多者不限,最后取光者胜。

要想制造必胜局,我们得让对面陷入一种必败态,然后维护这种必败态即可,所以我们可以想一下必败态是什么样的,最特殊的必败态是(0,0,0),这样的情况下是对方已经取完最后的物品,获胜了。再仔细分析一下(0,n,n)也是一种必败态,为什么呢 ?因为无论我选择取哪一堆的多少物品,对方只需要在另一堆上取相同数目的物品,保持(0,n,n)的局势,取到最后一定会变成(0,1,1)的局势,这样我只能取一个物品,剩下的那个物品对方就可以取了获胜。再仔细想想,(1,2,3)其实也是一种必败态,因为无论我如何取,取多少,对方都可以将局势变成(0,n,n)的必败态。

我们将这种局势统称为奇异局势,那这种奇异局势有什么特点呢?

也不知道是谁这么牛逼,竟然能把这种局势和二进制连续在一起

对于奇异局势来说,a ^ b ^ c其实是等于0的。第一种必败态就不用说了全是0,异或完了也是0,对于第二种,n^n其实也是0,因为任何数异或她本身得到的绝对是0,对于第三种(a,b,c)类的异或和也是0

如果我们面对的是一个非必败态(a,b,c),要如何变成必败态呢?

我们假设a < b <c,我们只需要将c变成a ^ b即可,因为这样的话c ^ (a ^ b)就是0了,符合必败态的规律,所以对于三堆物品,我们第一步只需要取c - (a ^ b)个物品即可

不知道,你看懂没……如果还是没懂,就给个面子装懂好嘛……

现在将尼姆博弈模型推广一下,有n个物品堆,两人人轮流在任意的物品堆取任意多的物品,最后取光者获胜

同样的(a,b,c,……n)堆物品,我们需要先求出abc……n的异或值,然后对每一堆进行判断,看需要取几个能构成必败态,也就是判断一下是否满足如下条件即可

if(tr[i] > (sum ^ tr[i]))

sum^tr[i]得到的是除了第i堆以外其他堆的数的异或和,如果我们能让第i堆的值等于剩下的所有值的异或和,那么他们异或起来的值肯定是0,即符合奇异局势

例题:E - Being a Good Boy in Spring Festival

思路:

先计算出所有数的异或值ans,然后循环跑一遍看看数里面有没有x能大于等于ans ^ x的

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n, tr[10005], sum, ans;
int main()
{
    while(cin>>n && n)
    {
        ans = 0;
        sum = 0;
        for(int i = 1; i <= n; i++)
        {
            cin>>tr[i];
            ans ^= tr[i];
        }
        for(int i = 1; i <= n; i++)
        {
            if(tr[i] > (ans ^ tr[i]))
                sum++;
        }
        cout<<sum<<endl;
    }
    return 0;
}

尼姆博弈变形一

问题:

现在有n堆石子,每人每次可以从中选任意一堆,取1<= x <= k颗石子,最后取光者胜

思路:

相比于普通的尼姆博弈,主要的区别是取的数量发生了改变,无法取任意数量的石子了,所以我们可以让他取“任意”数量的石子,就先对每一堆石子进行巴什博弈,也就是对每堆石子变成x % (k + 1)个,此时相对来说就可以取任意石子,就再来一次尼姆博弈即可

尼姆博弈变形二

问题:

现有n堆石子,每人每次可以从中选至多k堆,每堆中可以取任意颗石子,取光者胜

思路:

将每堆石子的个数以二进制的形式表示,用数组tr[i]统计每一位上1的个数,如果每一位1的个数都可以被(K + 1)整除,则先手必败,否则先手必胜。

反尼姆博弈

题目:

现有n堆石子,每人每次可以从中选任意一堆取任意数量石子,至少一颗,取光者输掉

思路:

先手必胜条件:

  • 每堆石子的数目均为1,且石子数异或和为0

  • 至死一堆石子的数量不为1,且石子数异或和不为0

阶梯尼姆游戏

问题:

在一个阶梯上,每层有若干个石子,每次可以将其中某层中若干个石子移动到他下一层阶梯中去,不能操作者失败

思路:

对奇数堆进行尼姆博弈,如果异或和为0,则先手必胜

为什么可以这样呢?

假设我们先手,且奇数阶梯的石子数异或和为0,也就是我们必胜,我们只需要按能赢的步骤将奇数堆堆石子移动到偶数堆……如果对手也是移动奇数堆,我们也继续移动奇数堆……如果对手将偶数堆堆石子移动到奇数堆,我们就可以将对手移动到奇数堆的那些石子移动到下一个偶数堆,相当于奇数堆石子不变,总的来看就相当于没有偶数堆,只是奇数堆的数在一直消失,也就可以抽象成尼姆博弈!

那么为什么只对奇数堆进行尼姆博弈呢?偶数堆为什么不行?

因为奇数的时候我们可以将对手移动到奇数堆的石子移动到偶数堆,极端情况下是从1 -> 0,而如果堆偶数堆的时候,极端情况是将0 -> -1,很显然没有-1层阶梯,就无法成功,只能破坏原先的nim,导致胜负关系不确定。

参考博客:https://www.cnblogs.com/solvit/p/11393639.html

祝大家新年快乐鸭!(好像貌似可能差不多有那一内内的晚,就一内内⁄(⁄ ⁄ ⁄ω⁄ ⁄ ⁄)⁄,主要是年后这几天又开始看小说了,这小说不看完心里就总有个疙瘩,就一不小心看了炒鸡炒鸡长的时间,也就鸽了好处时间没写博客,今天就重拾这个博弈,将他补全了,先应付一波(= ̄ ρ ̄=) ..zzZZ)

分类:

算法

技术点:

相关文章: