博弈论
组合游戏分类(下文有非回合制零和博弈)
公平组合游戏
- 1.游戏有两个人参与,知道游戏的所有信息。
- 2.在游戏的任意时刻,游戏者可以做出的决策只与当前游戏的状态有关,与游戏者无关。
- 3.游戏中的同一个状态不可能多次抵达,游戏以玩家无法行动为结束,且游戏一定会在有限步后以非平局结束。
非公平组合游戏
非公平组合游戏(Partizan Game)与公平组合游戏的区别在于在非公平组合游戏中,游戏者在某一确定状态可以做出的决策集合与游戏者有关。大部分的棋类游戏都 不是 公平组合游戏,如国际象棋、中国象棋、围棋、五子棋等(因为双方都不能使用对方的棋子)。
nim游戏
首先介绍一个经典的公平组合游戏——
取走最后一个物品的人获胜。经典的问题是对于
SG函数
定义
例如
对于状态
而对于由
这一定理被称作
例题:
eg1. P2197 【模板】nim 游戏
题意:
就是上文的nim游戏板子,有必胜策略输出“Yes”,否则输出“No”
解法:
我们只需要找到
我们可以将一个有
那么,由
根据上面的推论,可以得出
#include<bits/stdc++.h>
using namespace std;
int t;
int n;
int ans;
int a[10001];
int main(){
cin>>t;
while(t--){
cin>>n;
ans=0;
for(int i=1;i<=n;i++){
cin>>a[i];
ans^=a[i];
}
if(ans==0) cout<<"No"<<endl;
else cout<<"Yes"<<endl;
}
return 0;
}
eg2.[AGC002E] Candy Piles
题意:
桌上有
-
将当前最大的那堆糖果全部吃完
-
将每堆糖果吃掉一个
吃完的人输,假设两人足够聪明,问谁有必胜策略?
输出 First
(表示先手必胜)或 Second
(表示后手必胜)
解法:
把样例拿过来:
9
7 6 7 7 2 2 4 4 4
先从大到小排序:
9
7 7 7 6 4 4 4 2 2
可以把它输入位置当作x轴,大小当作y轴,得到下图:
第一种操作其实就是把最左边的一列删除,而第二种操作则是把最下面的一行删除。
从原点开始,每人每次可以选择向右或向上移动一格,向右代表消去最左边一行,向上代表消去最下面一行。很容易发现,当走到网格图的边界(下图中的实线部分)时,所有糖刚好被吃完。
不难理解边界上就是必败态。而如果一个点上方和右方都是必败态,这个点就是必胜态,否则是必败态。但是
通过仔细观察,我们发现一条对角线上的状态都是一样的,所以我们只需要关注原点所在对角线上的状态即可
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
int a[100002];
bool cmp(int a,int b){
return a>b;
}
int fin;
int pls1,pls2;
signed main(){
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
sort(a+1,a+1+n,cmp);
for(int i=1;i<=n+1;i++){
if(a[i]<i || a[i+1]<=i){
fin=i-1;
break;
}
}
int j=0;
for(;a[j+fin+1+1]==fin+1;j++) 1;
if(((a[fin+1]-fin-1&1) || (j&1))) cout<<"First";
else cout<<"Second";
return 0;
}
eg3.CF794E Choosing Carrot
题意:
下个月就是奶牛Z的生日啦,为了给奶牛Z购买生日礼物,奶牛A和奶牛B决定去挑选奶牛Z最喜欢的青草来作为送给奶牛Z的生日礼物。
现在,奶牛A和奶牛B买来了n堆青草,从左数起,第i堆青草的甜度为ai。奶牛A认为奶牛Z喜欢甜的青草,而奶牛B认为奶牛Z喜欢不甜的青草。因此,奶牛A希望选出来的青草是最甜的,奶牛B希望选出来的是最不甜的青草。
为了解决这个问题,奶牛A与奶牛B决定玩一个游戏,他们俩每次可以从两端的青草开始,选择其中一堆并把这一堆青草吃掉,最后剩下的那一堆青草就是送给奶牛Z的生日礼物,奶牛A先开始吃。
在玩游戏之前,奶牛B去上了一次厕所,奶牛A乘机进行了K次操作,每次操作也是按照要求从这些草堆当中,选择两端的草堆并吃掉其中一堆。在奶牛B回来之后,同样也是奶牛A先开始吃。
奶牛A想知道,对于每一个
解法:
先考虑A奶牛不先选的情况,因为奶牛
然后考虑进行
当
要么左右各拿一个
设
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
int a[300001];
int lf[300001];
int rf[300001];
int ans[300001];
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
ans[1]=max(ans[1],a[i]);
}
for(int i=1;i<=n;i++){
//int mid=(i+n)/2;
lf[min(i,n-i)]=max(lf[min(i,n-i)],max(a[i],a[i+1]));
rf[min(i-1,n-i)]=max(max(rf[min(i-1,n-i)],min(a[i-1],a[i])),min(a[i],a[i+1]));
}
for(int i=n/2;i>=1;i--){
ans[i*2]=max(ans[2*i+2],lf[i]);
ans[2*i+1]=max(ans[2*i+3],rf[i]);
}
for(int i=n;i>=1
;i--) cout<<ans[i]<<" ";
return 0;
}
nim游戏的变形
1.nim-k游戏
大致与
结论:把每一堆转化为二进制数。每个二进制位上为
证明:一篇blog
eg4.P2490 [SDOI2011] 黑白棋
题意:
小 A 和小 B 又想到了一个新的游戏。
这个游戏是在一个
最左边是白色棋子,最右边是黑色棋子,相邻的棋子颜色不同。
小 A 可以移动白色棋子,小 B 可以移动黑色的棋子,其中白色不能往左,黑色不能往右。他们每次操作可以移动
每当移动某一个棋子时,这个棋子不能跨越两边的棋子,当然也不可以出界。当谁不可以操作时,谁就失败了。
小 A 和小 B 轮流操作,现在小 A 先移动,有多少种初始棋子的布局会使他胜利呢?
输入三个数
输出小 A 胜利的方案总数。答案对
- 对于
的数据,有 。 - 对于
的数据,有 , 为偶数, 。
解法:
我们把相邻的黑白棋子之间的距离当作一堆石子的个数,转化为典型的
2.反nim
顾名思义游戏是相同的,只不过取走最后一个的是败者。
结论:先手必胜:1:存在偶数堆石子且石子数为1
2:至少存在一堆石子数大于1且石子数异或不为0
反之必败
证明:另一篇blog
eg5.LightOJ 1253 Misere NIM(反NIM博弈)
实在是找不到网址。
题意:
给定有
解法:
就是反nim游戏。
#include<bits/stdc++.h>
using namespace std;
int T,n,x,ans,cnt;
int main(){
scanf("%d",&T);
for(int cas=1;cas<=T;cas++){
scanf("%d",&n);
cnt=ans=0;
for(int i=1;i<=n;++i){
scanf("%d",&x);
if(x==1) cnt++;
ans^=x;
}
if(cnt==n){
if(ans==0) printf("Case %d: Alice\n",cas);
else printf("Case %d: Bob\n",cas);
}
else{
if(ans) printf("Case %d: Alice\n",cas);
else printf("Case %d: Bob\n",cas);
}
}
return 0;
}
3.阶梯nim游戏
现在,有一个n个台阶的楼梯,每一级台阶上有若干个石子。两位玩家轮流操作,每次操作可以将任意一级台阶上拿若干个石子放到下一级台阶中,已经拿到地面上的石子不能再拿,无法操作的人失败。
结论:奇数集异或和=0 ⟺ 必败;
下文是一道反方向的阶梯nim游戏
有N堆石子,除了第一堆外,每堆石子个数都不少于前一堆的石子个数。 两人轮流操作每次操作可以从一堆石子中移走任意多石子,但是要保证操作后仍然满足初始时的条件谁没有石子可移时输掉游戏。问先手是否必胜。
eg6.BZOJ 1115: [POI2009]石子游戏Kam (阶梯nim)
解法:
这一题的关键就是给出的序列是单调不降的。所以我们可以考虑对原序列差分。然后我们考虑拿走石子的意义其实就是减小该堆与前一堆的差值,加大该堆和后面的差值,倒着把奇数层异或起来。
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e6+10,INF=1e9+10;
int QWQ;
inline char nc(){
static char buf[MAXN],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,MAXN,stdin),p1==p2)?EOF:*p1++;
}
int a[MAXN];
int main(){
cin>>QWQ;
while(QWQ--){
int N,ans=0;
cin>>N;
for(int i=1;i<=N;i++) cin>>a[i];
for(int i=N;i>=2;i--) a[i]=a[i]-a[i-1];
for(int i=N;i>=1;i--)
if( (N-i+1)&1 ) ans=ans^a[i];
puts(ans?"TAK":"NIE");
}
return 0;
}
非回合制零和博弈
其实就是在组合游戏的基础上增加一个条件:对于一步操作,它会对一方带来收益,而另一方支付相应的损失。
不难得出:
- 1.在任意时刻,博弈双方的收益和恒为0
- 2.如果双方同时,对于对方的所有决策,一般不存在同一种最优决策。这时问题就变成了混合决策问题。
混合决策问题:
我们假设一位玩家有n种决策方式,另一位有m种决策方式。那么对于一位玩家A来说,他的收益情况是一个n * m 的矩阵。对于这个矩阵里A的收益e[i][j],B的对应收益为-e[i][j]。
怎么决策:
显然双方的最终目标是最大化自己的收益(相当于最大化对方的支出)。直观上来说。若A选择了决策a,相应的B会选择决策b使得e[a][b]取得最小值。直观上来说,我们要选取一个概率序列{l1,l2,l3....,ln}(A选择1-n决策的概率)使得
纳什平衡:
本文提到的问题为混合策略纳什均衡,纳什平衡其实还分为纯策略纳什均衡。
举一个最典型的问题(钱币问题)
硬币正反问题:
假如你正在图书馆枯坐,一位陌生美女主动过来和你搭讪,并要求和你一起玩个数学游戏。美女提议:“让我们各自亮出硬币的一面,或正或反。如果我们都是正面,那么我给你3元,如果我们都是反面,我给你1元,剩下的情况你给我2元就可以了。”那么该不该和这位姑娘玩这个游戏呢?这基本是废话,当然该。问题是,这个游戏公平吗?
假设我们出正面的概率是x,反面的概率是1-x,美女出正面的概率是y,反面的概率是1-y。为了使利益最大化,应该在对手出正面或反面的时候我们的收益都相等(不然在这个游戏中,对方可以改变正反面出现的概率让我们的期望收入减少),由此列出方程就是:
解方程得
同样,美女的收益,列方程:
解得
具体求解可以看:基础博弈论学习笔记
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理