博弈论基础


博弈论

定义:有若干个人进行博弈,每人轮流操作,且每个人的每一步都是最正确的操作,问什么时候必败/必胜

模型:

建模

①设先手必胜局面为\(N\),先手必败局面为\(P\),于是就有了一些结论

  1. 如果当前局面为\(N\),那么它肯定变化成\(P\),因为我们要必胜,那么对手必须进入一先手必败的\(P\)中,于是\(N\)必定能变化成\(P\)
  2. 同理,一个\(P\)肯定能变化成\(N\)

②于是我们就可以开始建图了

  1. 设每一个状态都为一个节点,我们就可以从当前节点像每一个能到达的点,连一条有向边
  2. 于是我们已知游戏结束的状态是\(P\),然后从这个状态扩展出去,如果一个节点能到底\(P\)那么它就是\(N\), 如果能到到达的每一个点都是\(N\),即不能到底\(P\),那么则为\(P\)
    ③我们的博弈论基本模型就建好了,其实每一个博弈论问题都可以转换为一个\(DAG\)

\(SG\)定理和\(SG\)函数

\(mex\)运算:

对于一个集合\(S\),它的\(mex(S)\)就为最小的、不属于集合\(S\)的非负整数

\(SG(x)\)

对于\(SG(x)\),有\(SG(x)=mex(SG(y),<x,y>∈E)\),其实就是它在\(DAG\)图中所有后继状态的\(mex\)

\(SG(x)\)的拆分

如果\(x\)可以拆分成互不影响的几个子游戏\(x_1,x_2,...x_n\),那么\(SG(x)=SG(x_1)⊕SG(x_2)⊕...⊕SG(x_n)\)

\(SG(x)\)与胜负

\(SG(x)=0\)为先手必败,否则先手必胜


常见模型

巴什博奕(\(Bash Game\)

题目大意:有\(n\)个石子,每次少取\(1\)个,最多取\(m\)个,问先手必胜还是必败

分析:(正常分析)

  1. 假设我们面对了一个\((m+1)|n\)的情况,那么假设我们取了\(x\)个,那么对手肯定能取\(m+1-x\)个,使得对手\(+\)我们的和为\(m+1\),因为\((m+1)|n\),所以对手肯定能取到最后一个,所以当\((m+1)|n\)时,先手必败
  2. 同理如果\((m+1) \nmid n\),则我们可以先取走\(n\%(m+1)\)个,使得对手面临\((m+1)|n\),那么这时先手必胜

代码实现:

#include<bits/stdc++.h>
using namespace std;
int t;
int main()
{
	cin >> t;
	while (t--)
	{
		int n,k;cin >> n >> k;
		if (n%(k+1)==0) printf("B\n");
		else printf("A\n");	
	}	
}

威佐夫博弈(\(Wythoff Game\)

题目大意:有两堆石头,数量分别为\(n,m\),每次可以在一堆,或者两堆中,同时取任意个石头,不能不取,问先手必胜还是必败

分析:(打标,找规律)

  1. 我们列举前几个先手必败的情况看一些,\((0,0),(1,2),(3,5),(4,7),(6,10)...\),对于以\(n\)排序之后的数对,我们发现\(n_i\)为之前所有数对中未出现的最小自然数,\(m_i\)\(n_i+i-1\),
  2. 其实我们就可以暴力算出当前局面是什么了,但有大佬又证明出了,每个先手必败的局面的数对都是\(([i×ϕ],[i×ϕ^2])\)\(ϕ=\frac{\sqrt5-1}{2}\)证明)(%%%

代码实现:

#include<iostream>
#include<cmath>
using namespace std;
const double phi=(sqrt(5)+1)/2;
int a,b,t;
int main()
{
	cin >> t;
	ios::sync_with_stdio(false);
	while (t--)
	{
		cin >> a >> b;
		if (a>b) swap(a,b);
		int A=abs(a-b)*phi;
		if (A==a) cout << "B"<< endl;
		else cout << "A" << endl;
	}
	return 0;
}

尼姆博弈(\(Nim Game\))

题目大意:有\(n\)堆石子,每堆石子有\(A_i\)个石子,两个人轮流操作,每次从一堆石子中,取任意个,不能不取,问先手必胜还是必败

分析:

  1. \(A_1 ⊕ A_2 ⊕ ... ⊕ A_n\not=0\),那么必然有\(A_i \geqslant x\)(异或的性质),所有说只要将\(A_i\)减成\(x\),那么就可以使得\(A_1 ⊕ A_2 ⊕ ... ⊕ A_n=0\),于是一个异或和不为\(0\)的一定可以到底一个异或和为\(0\)
  2. \(A_1 ⊕ A_2 ⊕ ... ⊕ A_n=0\),如果可以将\(A_i\)变成\(A_i^{'}\)使得\(A_1 ⊕ A_2 ⊕ ... ⊕ A_n\not=0\),那么将两个式子异或,得到\(A_i ⊕ A_i^{'}=0,A_i=A_i^{'}\),矛盾,所以异或和为\(0\)的一定不可以保持异或和为\(0\)
  3. 因为所有石子都被取完时为\(P\),而且石头是不断减少的,所以当异或和为\(0\)是先手必败,否则先手必胜

代码实现:

#include<bits/stdc++.h>
using namespace std;
int a[10010],n,t;
int main()
{
	cin >> t;
	while (t--)
	{
		int ans;
		cin >> n;
		for (int i=1;i<=n;i++) cin >> a[i];
		ans=a[1];
		for (int i=2;i<=n;i++) ans=ans^a[i];
		if (ans==0) printf("No\n");
		else printf("Yes\n"); 
	}
}


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM