[博弈论]初探(巴什、威佐夫、尼姆、斐波那契)

借鉴了大佬的资料:
博弈论.

一、巴什博弈(Bash Game)

引例,最古老的一个游戏:

A和B一块报数,每人每次报最少1个,最多报4个,看谁先报到30(第30个数)。

分析

第一次报数,A报出数的个数是 x ,那么 B报数 5-x 个,接着现在问题变成了下面这个样子:
A 和 B 一块报数,看谁先报到第25个
然后问题逐渐简化,规模缩小:20、15、10、5。最后问题是看谁先报到 5,这时无论 A 怎么报数,最后一个数一定是 B 报出来的,所以 B 必胜。从这里的分析可以得到:
作为后手的 B 在这次游戏中是不会输的。

归纳

如果我们要报 n 个数,每次最少报 1 个,最多报 m 个,可以找出这么一个整数 k 和 r ,使得 n = k * (m+1) + r,代入引例中可知,如果 r = 0 ,那么先手必败,否则,先手必胜(这里我还不太会证明。。。)。

巴什博弈(一般化)

问题描述

只有一堆 n 个物品,两个人轮流从中取物,规定每次最少取 1 个,最多取 m 个,最后取光者为胜

代码示例

#include<iostream>
using namespace std;
int main()
{
	int n, m;
	while (cin >> n >> m)
	{
		if (n % (m + 1) == 0)
		{
			cout << "后手必胜" << endl;//先手必败
		}
		else
		{
			cout << "先手必胜" << endl;
		}
	}
	return 0;
}

二、威佐夫博弈(Wythoff Game)

问题描述

有两堆各若干的物品,两人轮流从其中一堆取至少 1 件物品,至多不限,或从两堆中同时取相同件物品,规定最后取完者胜利。

解题结论

若两堆物品数量的初始值是 (x, y),且 x < y,则令 z = y - x;
设 *w=(int)[((sqrt(5)+1)/2)z ];
(下图是人话)
在这里插入图片描述
若 w = x,先手必败,否则后手必败;

代码示例

#include<iostream>
#include<math.h>
using namespace std;

int main()
{
	int n1, n2, temp;
	while (cin >> n1 >> n2)
	{
		if (n1 > n2) //保证 n1 <= n2
		{
			swap(n1, n2);
		}
		temp = floor((n2 - n1) * (sqrt(5.0) + 1) / 2.0);
		if (temp == n1)
		{
			cout << "后手必胜" << endl;
		}
		else
		{
			cout << "先手必胜" << endl;
		}
	}
	return 0;
}

三、尼姆博弈 (Nimm Game)

问题描述

有任意堆物品,每堆物品的个数是任意的,两个人轮流从中取物品,每一次只能从一堆物品中取部分或全部物品,最少取 1 件,取到最后一件物品的人获胜。

解题结论

把每堆物品数全部异或起来,如果得到的值为 0 ,那么先手必败(后手必胜),否则先手必胜。

代码示例

#include<iostream>
#include<math.h>
using namespace std;
int main()
{
	int i, n, ans, temp;
	// n 堆物品
	while (cin >> n)
	{
		temp = 0;
		for ( i = 1; i <= n; i++)
		{
			cin >> ans;
			temp ^= ans; //我们把每一堆的物品数给异或起来
		}
		if (temp == 0)
		{
			cout << "后手必胜" << endl;
		}
		else
		{
			cout << "先手必胜" << endl;
		}
	}
	return 0;
}

四、斐波那契博弈

问题描述

有一堆物品,两人轮流取物品,先手最少取一个,至多无上限,但是不能把物品取完,之后每次取得物品数不能超过上一次取的物品数的 2 倍且至少为 1 件,取走最后 1 件物品的人获胜。

解题结论

当且仅当 n 是斐波那契数(n 为物品总数)时,先手必败。

代码示例

#include<iostream>
#include<math.h>
using namespace std;
#define N 55
int f[N] = {}; //斐波那契数列

void Init();

int main()
{
	bool flag = false; // true : 先手必败,flase : 后手必败
	int n, i;
	Init();
	while (cin >> n)
	{
		if (n == 0)//不用玩了
		{
			break;
		}
		flag = false;
		for ( i = 0; i < N; i++) //判断是否是斐波那契数,只好一个一个试
		{
			if (n == f[i])
			{
				flag = true;//先手必败
				break;
			}
		}
		if (flag)
		{
			cout << "后手必胜" << endl;
		}
		else
		{
			cout << "先手必胜" << endl;
		}
	}
	return 0;
}

void Init()
{
	int i;
	f[0] = 1;
	f[1] = 1;
	for ( i = 2; i < N; i++)
	{
		f[i] = f[i - 1] + f[i - 2];
	}
}

总结

可以注意到以上所有博弈问题
1、获胜条件都是取到最后一件物品;
2、如果结果满足对应条件,先手必败;

猜你喜欢

转载自blog.csdn.net/weixin_44092088/article/details/110305734