当前位置:   article > 正文

【博弈论】模板总结 + 例题 【补】_博弈论的开放性题目设计

博弈论的开放性题目设计

这篇博主要记录了巴什博弈、威佐夫博弈、斐波那契博弈、尼姆博弈与Grundy数(SG函数)等博弈论相关学习笔记。

博弈论题目的主要特点:

  • 有两名选手
  • 两名选手交替操作,每次只能走一步,每步都必须合法
  • 在任何情况下,合法操作只取决于情况本身,与选手无关
  • 游戏失败的条件是:某位选手没有可以进行的合法操作

在推到几种博弈的前提是你自己必须想:我是先手!!我一定要赢!!拿出自己的最优策略。

以下是几种常见的博弈题型:

巴什博弈

一堆n个物品,两名选手轮流从中取出1~m个,最后取光者胜。用一个数字(x)表示一堆物品中剩下的数量。

还剩0个物品是一种必败态。当物品还剩下0个、就是一种必输的局势,被称为必败态。

还剩m+1个物品也是一种必败态。因为如果你最多取m个,留给后手的一定小于或等于m个,所以这也是必败态。

接着我们来讨论物品还剩(m+1+k)的情况:

  • k=0,转化为m+1,是一种必败态。
  • k>0时,有两种情况,第一种是0<k<=m,这是一种必胜态,一次性拿走k个,然后留给对手m+1个,给对手必败态。第二种情况是k>m,有k=t(m+1)+r,其中t>=0、0<=r<=m,所以我们很明显可以看出,如果局面是t(m+1)相当于进行了t次(m+1)操作,是一种必败态;又因为0<=r<=m,一次性取完r,留给对手必败态,自己就是必胜态。

所以,综上所述:若n%(m+1)==0先手必败、反之先手必胜。

核心代码:

  1. int n,m;
  2. cin >> n >> m;
  3. if(n%(m+1)==0)
  4. cout << "先手必败!\n";
  5. else
  6. cout << "先手必胜!\n";

威佐夫博弈

有两堆各若干个物品,两人轮流从某一堆或同时从两堆中取同样多的物品,每次至少取一个、多的不限。A先手。

我们用(ak,bk)表示两堆物品数量局势,若A面对(0,0),那么A输,这种先手必输的局势,我们称为奇异局势。同样的我们可以得到(0,0)、(1,2)、(3,5)、(4,7)、(6,10)、(8,13)、(9、15)、(11,18),(12,20)等等。我们在这些局势前很容易发现ak是未在前面出现过的最小自然数,又有:bk=ak+k。我们的ak也可以由[k*(√5+1)] / 2。

所以我们先求出差,差值*黄金分割比==最小者后手赢、否则先手赢。

核心代码:

  1. doublee r = (sqrt(5)+1)/2;
  2. int d = abs(a-b)*r;
  3. if(d!=min(a,b)) return ture;
  4. else return false;

斐波那契博弈

一共n个物品,两人轮流取出1~任意个,但是不能取完,以后每次取的物品都不能超过上一次的2倍,取完者胜。

我们令n代表,还有多少个物品,试着推导一下:

n=2,后手win。先手取一个,后手就能全部取完。

n=3,后手win。先手无论取一个还是两个,后手都可以一次性取完。

n=4,先手win。若先手取一个,后手剩3个,状态转移至n=3,当n=3时,后手先取,先手win。

n=5,后手win。先手取一个,剩余4个,状态转移至n=4,后手先取,后手赢。若先手取两个或者三个,后手可以一下全部取完获胜。

n=6,先手win。先手取一个,剩余5个,状态转移n=5,后手先取,先手赢。

n=7,先手win。先手取两个,状态转移至n=5,后手先取,先手赢。

n=8,后手win。先手取一个,状态转移n=7,后手赢。

等等等等,不再陈述……

我们不难发现,若n是斐波那契数列,后手赢。反之先手赢。

核心代码:

  1. f[0]=f[1]=1;
  2. for(int i=0;f[i-1]<n;i++){
  3. f[i]=f[i-1]+f[i-2];
  4. if(f[i]==n) return true;
  5. }
  6. return false;

尼姆博弈

有n堆物品,两人轮流取,每次取某堆中不少于一个,最后取完者胜。

 要判断游戏的胜负,只需用异或运算就好了。(也不知道哪位神仙发明的如此神奇的算法)。

a1 XOR a2 XOR ……  XOR an !=0 先手赢,若==0后手赢。

核心代码:

  1. int res=0;
  2. for(int i=0;i<N;i++)
  3. res ^= A[i];
  4. if(res!=0) cout << "先手win!\n";
  5. else cout << "后手win!\n";

 

Grundy数(SG函数)

有n堆物品,只能按规定集合拿,谁先取完谁获胜!这个东西压轴出场超级神奇!用的好可以解决以上所有问题!

emmm……由于递推过程比较复杂,在这里也不做过多阐述,我感觉套模板就可以了,有兴趣的读者可以前往以下链接:

https://blog.csdn.net/Dog_dream/article/details/80445886     (写的比较详细)

核心代码:

  1. int n,k,x[MAXN],a[MAXN];
  2. int grundy[MAXN+1];
  3. void solve(){
  4. grundy[0]=0;
  5. int max_x = *max_element(x,x+n);
  6. for(int j=1;j<=max_x;j++){
  7. set<int> s;
  8. for(int i=0;i<k;i++){
  9. if(a[i]<=j) s.insert(grundy[j-a[i]]);
  10. }
  11. int g=0;
  12. while(s.count(g)!=0) g++;
  13. grundy[j]=g;
  14. }
  15. int res=0;
  16. fpr(int i=0;i<n;i++)
  17. res^=grundy[x[i]];
  18. if(res!=0) cout << "First win!\n";
  19. else cout << "Second win!\n";
  20. }

例题:

HDU 2516 取石子游戏 【传送门】

HDU 1527 取石子游戏 【传送门】

HDU 2188 悼念512汶川大地震遇难同胞——选拔志愿者 【传送门】

HDU 1846 Brave Game 【传送门】

HDU 1850 Being a Good Boy in Spring Festival 【传送门】

HDU 2176 取(m堆)石子游戏 【传送门】

HDU 1847 Good Luck in CET-4 Everybody! 【传送门】

HDU 1525 Euclid's Game  【传送门】

HDU 1848 Fibonacci again and again 【传送门】

待解决:

POJ 1082 Calendar Game 

HDU 1564 Play a game

HDU 2897 邂逅明下

HDU 3032 Nim or not Nim ?

…………慢慢扩展队列,今天不想一个一个粘贴了……

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/471388
推荐阅读
相关标签
  

闽ICP备14008679号