【算法竞赛学习笔记】基础博弈-数学提升计划


title : 基础博弈
date : 2021-8-4
tags : ACM,博弈论
author : Linno

【算法竞赛学习笔记】基础博弈-数学提升计划_第1张图片

基本概念

ICG游戏:两个人参与的游戏,轮流做出对中间最有利的决策。

必败态:P-position(无法转移的状态),处于这种状态的人必输。

必胜态:N-position(可以转移到P的局面),处于这种状态的人必胜。

巴什博弈

一堆n个物品,两个人从中轮流取1~m个,最后不能继续取的人输。

同余定理:n=k*(m+1)+r;先取者拿走r个,那么后者无论拿走1~m个先者只要拿的数目之和为m+1那么先手就必赢。反之若n=k*(m+1),那么先者无论怎么样都会输。

代码
if(n%(m+1)) return false;
else return true;

威佐夫博弈

有两堆各若干个物品,两个人轮流从任意一堆中取出至少1个或者同时从两堆中取出同样多的物品,规定每次至少取1个,至多不限,最后取光者胜利。

代码
double r=(sqrt(5.0)+1)/2;
int d=abs(a-b)*r;
if(d!=min(a,b)) return true;
else return false;
简单的思考

用数对(x,y)来表示两堆物品的数量,对于一个必败态(n,m)与(m,n)由于其本质相同。最简单的,显然(0,0)的情况为必败态。

假如局势为(1,2),那么先手只有4中取法:

(1)先手取走第一堆的1个,那么后手取走第二堆的2个,后手胜。

(2)先手取走第二堆的2个,那么后手取走第一堆的1个,后手胜。

(3)先手取走第二堆的1个,那么后手在第一第二堆各取1个,后手胜。

(4)先手在第一第二堆各取1个,那么后手在第二堆取1个,后手胜。

可见这是先手的必败态。

定义先手必输的局势为奇异局势,前几个奇异局势为(0,0),(1,2),(3,5),(4,7),(6,10)…

性质(不会证)

1、x为前1…k个奇异局势中没有出现过的最小正整数,y=x+k

2、任何一个自然数都包含在一个且仅有一个奇异局势中。

3、任何操作都会将奇异局势变为非奇异局势

4、可以采用适合的方法将非奇异局势变为奇异局势。

根据Beatty定理(记得回去补证明),假设两堆石子为(x,y)(其中x

那么先手必败,当且仅当 ( y − x ) ∗ 5 + 1 2 = x (y-x)*\frac{\sqrt{5}+1}{2}=x (yx)25 +1=x

(黄金分割数1.618*两堆的差=最小值时为先手必败态)

尼姆博弈(Nim Game)

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

结论:将n堆物品数量全部异或后结果为0则必败,否则必胜。

尼姆博弈的结论是非常重要的,但证明过程较为繁琐。

代码
int res=0;
for(int i=1;i<=n;i++) res=res^arr[i];
if(res) return true;
else return false;

对于一般的博弈论问题,我们可以尝试通过SG函数转化为Nim Game

SG(Sprague-Grundy)函数

首先给出一种ICG博弈游戏模型,给定一个有向无环图和一个起始顶点上的一枚棋子,两名选手交替的将这枚棋子沿有向边进行移动,无法移动者判负。

将ICG问题进行转换:任何一个ICG都可以通过把每个局面看成一个顶点,对每个局面和它的子局面连一条有向边来抽象成这个“有向图游戏”。

于是我们可以通过将ICG问题转换为上述这个游戏,再通过寻找这个游戏的一遍解法来解决ICG问题。

首先定义mex(minimal excludant)运算,这是施加于一个集合的运算,表示最小的不属于这个集合的非负整数。例如mex{0,1,2,4}=3,mex{2,3,5}=0,mex{}=0;

对于一个给定的有向无环图,定义关于图的每个顶点的SG函数如下:

sg(x)=mex{sg(y)|y是x的后继}

步骤

一、找出必败态(SG值为0)

二、找到当前所有状态的前驱节点

三、根据定义计算节点SG值

重复上述步骤,直到整棵树建立完成

SG定理

游戏的和的SG函数值是它的所有子游戏的SG函数值的异或。

因此,当我们面对n个不同的游戏组成的游戏时,只需求出每个游戏的SG函数值把这些SG值全部看成Nim的石子堆,然后依照找Nim的必胜策略的方法来找这个游戏的必胜策略。

模板
//f[i]:可改变当前状态的方式,需要预处理
//S[i]:为i后继状态的集合
void getSG(int n){
	int i,j;
	memset(SG,0,sizeof(SG));
	for(i=1;i<=n;i++){
		memset(S,0,sizeof(S));
		for(j=0;f[j]<=i&&j<=N;j++) S[SG[i-f[j]]]=1;
		for(j=0;;j++) if(!S[j]){ //mex
			SG[i]=j;
			break;
		}
	}
}
Fibonacci again and again
#include
#define inf 0x3f3f3f3f
#define int long long
using namespace std;
const int N=1e3+7;
int m,n,p,f[N],SG[N],S[N];

void get_SG(){
	memset(SG,0,sizeof(SG));
	for(int i=1;i<=1000;i++){
		memset(S,0,sizeof(S));
		for(int j=1;f[j]<=i&&j<=N;j++) S[SG[i-f[j]]]=1;
		for(int j=0;;j++) if(!S[j]){
			SG[i]=j;
			break;
		}
	}
}

signed main(){
	f[0]=1;f[1]=1;
	for(int i=2;i<=100;i++) f[i]=f[i-1]+f[i-2];
	get_SG();
	cin>>m>>n>>p;
	while(m!=0||n!=0||p!=0){
		int ans=SG[m]^SG[n]^SG[p];
		if(ans) puts("Fibo");
		else puts("Nacci");		
		cin>>m>>n>>p;
	}
	return 0;
}

参考资料

https://www.bilibili.com/video/BV15b411W73J

https://www.cnblogs.com/zwfymqz/p/8469863.html

https://blog.csdn.net/qq_41311604/article/details/79980882

https://www.bilibili.com/video/BV1MT4y1L7BX

https://www.cnblogs.com/zwfymqz/p/8469840.html

你可能感兴趣的:(ACM,数学,博弈论,算法,博弈论,acm竞赛,程序设计,数学)