博弈论
前言
- 本文中的博弈论的详细介绍与讲解主要针对于信息学竞赛,可能存在部分观点与广义相对论有所矛盾。
- 部分资料来源于网络,已标明来源。
- 转载或引用请标明出处。
一、要素
- 局中人:在一场竞赛或博弈中,每一个有决策权的参与者成为一个局中人。只有两个局中人的博弈现象称为“两人博弈”,而多于两个局中人的博弈称为“多人博弈”。
- 策略:一局博弈中,每个局中人都有选择实际可行的完整的行动方案,即方案不是某阶段的行动方案,而是指导整个行动的一个方案,一个局中人的一个可行的自始至终全局筹划的一个行动方案,称为这个局中人的一个策略。如果在一局博弈中局中人都总共有有限个策略,则称为“有限博弈”,否则称为“无限博弈”。
- 得失:一局博弈结局时的结果称为得失。每个局中人在一局博弈结束时的得失,不仅与该局中人自身所选择的策略有关,而且与全局中人所取定的一组策略有关。
- 对于博弈参与者来说,存在着一博弈结果 。
摘自百度百科。
二、经典模型
从目录中跳转。
-
巴什博弈(
) -
威佐夫博弈(
) -
尼姆游戏(
) -
SG 函数
A. 巴什博弈
双人博弈。
问题
有一堆总数为
结论
若
注:这里的必胜以及下文的均表示有必胜策略。
简证
设
当
当
Code:
点击查看代码
namespace Bash
{
short main()
{
int n,m;
scanf("%d%d",&n,&m);
if(n%(m+1)==0) printf("Player2 win.\n");
else printf("Player1 win.\n");
return 0;
}
}
B. 威佐夫博弈
双人博弈。
问题
有两堆石子,两名玩家每次可以从任意一堆石子中取任意多的石子或者从两堆石子中取同样多的石子,最后取完者胜。
结论
枚举观察可以发现,在遇到特定的局势时,先手必败,我们称其为奇异局势。
在 OI 中应用较少。
判断奇异局势
证明见 百度百科。
设当前局势为
转换成判断形式为
Code:
这道题卡精度,需要手写 sqrt 并且调用 std 库才可 AC。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
namespace Wythoff
{
long double Wsqrt(long double n)
{
long double s=sqrt(n);
for(int i=1;i<=n;i++) s=n/s;
return s;
}
short main()
{
long long x,y,z;
scanf("%lld%lld",&x,&y);
z=abs(x-y)*((Wsqrt(5)+1)/2.0L);
if(z==min(x,y)) printf("0\n");
else printf("1\n");
return 0;
}
}
int main(){return Wythoff::main();}
C. 尼姆游戏
双人博弈。
问题
有若干堆石子,每堆石子的数量都是有限的,合法的移动是选择一堆石子并拿走若干大于零颗,最后取完者胜。
结论
设一种局势的状态为
简证
定义 P-position(Previous) 为后手必胜局势,简称 P 状态;N-position(Next) 为先手必胜局势,简称 N 状态。
我们将每一种状态视为一个节点,并将其向其后继状态连边,这样就得到了一个博弈状态图。
那么显然的是,入度为
那么通过推理,我们还可以得到两条定理:
-
一个状态为 N 状态当且仅当存在至少一个 P 状态为它的后继状态。
-
一个状态是 P 状态当且仅当它的所有后继状态均为 N 状态。
解释一下。
对于定理 1,若该状态存在至少一个 P 状态,那么玩家可通过操作到达该状态,使对手进入 P 状态,进而自己必胜。
对于定理 2,若该状态的所有后继状态都为 N 状态,即无论如何操作都会使对手进入 N 状态,进而自己必败。
若该博弈状态图是一个有向无环图,那么根据这些已知定理,我们可以用
但是复杂度太高,于是我们考虑结论中
若想得到该结论,只需证明一下两个定理:
-
对于
的状态,存在至少一种后继状态使得 。 -
对于
的状态,所有的后继状态的 值均不为 。
来自 OI-wiki。
Code:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int main()
{
int T;cin>>T;
while(T--)
{
int n,ans=0;cin>>n;
for(int i=1;i<=n;i++)
{
int a;cin>>a;ans^=a;
}
if(ans==0) printf("No\n");
else printf("Yes\n");
}
return 0;
}
应用——SG 函数
引入
定理
定义 mex 函数为不属于集合 S 中的最小自然数,即
对于一个状态 x 和它的所有后继状态
对于由
- 一个游戏的 SG 函数
讲一个游戏 拆分成若干个子游戏 ,那么该游戏的 SG 函数值为:
简证
摘自 OI-wiki。
三、例题
A. Game On Tree
很基础的 SG 函数应用题,只需要求出整棵树的 SG 值即可。
但真的只是用求所有子树的 SG 值异或和吗?
仔细观察,这道题其实可以转化为树上的关于边的尼姆游戏,每一棵子树到根还有一条未被算入的边,因此正确的结果应该为:
Code:
点击查看代码
#include<bits/stdc++.h>
const int Ratio=0;
const int N=2e5+5;
int n,m;
int hh[N],ne[N<<1],to[N<<1],cnt;
namespace Wisadel
{
void Wadd(int u,int v)
{
to[++cnt]=v;
ne[cnt]=hh[u];
hh[u]=cnt;
}
int Wdfs(int u,int fa)
{
int sum=0;
for(int i=hh[u];i!=-1;i=ne[i])
if(to[i]!=fa) sum^=Wdfs(to[i],u)+1;
return sum;
}
short main()
{
memset(hh,-1,sizeof hh);
scanf("%d",&n);cnt=0;
for(int i=1,a,b;i<n;i++)
scanf("%d%d",&a,&b),
Wadd(a,b),Wadd(b,a);
if(Wdfs(1,0)) printf("Alice\n");
else printf("Bob\n");
return Ratio;
}
}
int main(){return Wisadel::main();}
B. Alice 和 Bob 又在玩游戏
可以显然看出这是一个用 SG 函数求解的问题,显然我们只需对每个连通块计算一遍其 SG 值异或起来检验是否非零即可。
这道题删的边是与根节点联通的边,也就是删边操作后会形成森林,这些森林也就是每个状态的后继。我们发现,不同的树之间的操作互不影响,将它们看做不同的游戏,那么该后继状态的 SG 值其实就是每一个游戏的 SG 值的异或和。
根据 SG 定理,我们的任务转化成了快速求出所有树的 SG 值的mex 值。
考虑用 0-1trie 维护,可以达到
四、末
咕。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探