【博弈论】模板总结 + 例题 【补】

10 篇文章 0 订阅
8 篇文章 0 订阅

这篇博主要记录了巴什博弈、威佐夫博弈、斐波那契博弈、尼姆博弈与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先手必败、反之先手必胜。

核心代码:

int n,m;
cin >> n >> m;
if(n%(m+1)==0)
    cout << "先手必败!\n";
else
    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。

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

核心代码:

doublee r = (sqrt(5)+1)/2;
int d = abs(a-b)*r;
if(d!=min(a,b)) return ture;
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是斐波那契数列,后手赢。反之先手赢。

核心代码:

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

尼姆博弈

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

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

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

核心代码:

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

 

Grundy数(SG函数)

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

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

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

核心代码:

int n,k,x[MAXN],a[MAXN];
int grundy[MAXN+1];
void solve(){
	grundy[0]=0;
	int max_x = *max_element(x,x+n);
	for(int j=1;j<=max_x;j++){
		set<int> s;
		for(int i=0;i<k;i++){
			if(a[i]<=j) s.insert(grundy[j-a[i]]);
		}
		int g=0;
		while(s.count(g)!=0) g++;
		grundy[j]=g;
	}
	int res=0;
	fpr(int i=0;i<n;i++)
		res^=grundy[x[i]];
	if(res!=0) cout << "First win!\n";
	else cout << "Second win!\n";
}

例题:

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 ?

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值