博弈论学习笔记

决定近段时间复习一下博弈论顺便写点笔记。

大佬博客:几种常见博弈模型https://blog.csdn.net/wr132/article/details/51213331

SG函数与SG定理https://www.cnblogs.com/ECJTUACM-873284962/p/6921829.html

无敌的博弈总结https://blog.csdn.net/acm_cxlove/article/details/7854526

 

常见博弈模型

首先要清楚博弈论的基础知识概念(必胜必败态,多堆游戏,公平组合游戏,有向图游戏),然后了解几种常见的博弈模型,其中中比较常见的应该就是巴什博弈和尼姆博弈了。把一些题目转换为经典的博弈论模型能够快速地找到规律解决问题。

巴什博弈:只有一堆n个物品,两个人轮流从这堆物品中取物,规定每次至少取一个,最多取m个,最后取光者得胜。n%(m+1)=0,是必先手败的局势。

威佐夫博奕:有两堆各若干个物品,两个人轮流从某一堆或同时从两堆中取同样多的物品,规定每次至少取一个,多者不限,最后取光者得胜。奇异局势:ak =[k(1+√5)/2],bk= ak + k先手必败。

尼姆博弈:有三堆各若干个物品,两个人轮流从某一堆取任意多的物品,规定每次至少取一个,多者不限,最后取光者得胜。(a,b,c)是必败态等价于a^b^c=0。

 HDU-2149

巴什博弈裸题,问是否先手必胜,是的话输出先手第一次出手的必胜策略。m%(n+1)不为0先手必胜,必胜策略分两种如果m<=n那么先手就可以一次胜利,否则就第一次就要出m%(n+1)。

#include<bits/stdc++.h>
using namespace std;int main()
{int m,n;while (scanf("%d%d",&m,&n)==2) {if (m%(n+1)==0) {puts("none"); continue;}if (m<=n) {printf("%d",m); for (int i=m+1;i<=n;i++) printf(" %d",i);} else {printf("%d",m%(n+1));}puts("");}return 0;
}
View Code

POJ-1067

威佐夫博奕裸题,只有奇异局势的时候是必败态,奇异局势的公式为 ak =[k(1+√5)/2],bk= ak + k 。我们怎么判断呢?用bk-ak得到k,然后用这个k计算得到ak看看和原ak是否相等。

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;int main()
{int n,m;while (scanf("%d%d",&n,&m)==2) {if (m>n) swap(n,m);int k=n-m;int tmp=floor(k*(1.0+sqrt(5.0))/2);if (tmp==m) puts("0"); else puts("1");}return 0;
}
View Code

HDU-1847

巴什博弈变形。我们观察到3是一个先手必败点,所以大家都避免走到3,但是只要有一个人避免了必败点他就可以通过对方出的数凑成3的倍数让对方必败。所以n是3的倍数时候先手必败否则先手必胜。

#include<bits/stdc++.h>
using namespace std;int main()
{int n;while (scanf("%d",&n)==1) {if (n%3) puts("Kiki"); else puts("Cici");}return 0;
}
View Code

 

 

 SG函数与SG定理

但是对于一些非常见博弈模型或者是模型变形规律不明显的,我们有一个大杀器:SG函数。通过SG函数我们能够快速直到某个点是必胜/必败态从而找到规律。

做了HDU-1848当作求SG函数的模板:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N=1e3+10;
 4 int f[30],S[N],SG[N]; 
 5 
 6 void getSG(int n) {
 7     memset(SG,0,sizeof(SG));
 8     for (int i=1;i<=n;i++) {
 9         memset(S,0,sizeof(S));  //后继SG状态函数集合 
10         for (int j=0;j<=20&&f[j]<=i;j++) S[SG[i-f[j]]]=1;  //标记后继状态SG函数 
11         for (int j=0;;j++) 
12             if (!S[j]) {  //mex:后继SG第一个没出现的 
13                 SG[i]=j; break;
14             }
15     }
16 }
17 
18 int main()
19 {
20     int n,m,k;
21     f[0]=f[1]=1;
22     for (int i=2;i<=20;i++) f[i]=f[i-1]+f[i-2];  //求出可操作集 
23     getSG(1000);  //计算前1000的SG函数 
24     while (scanf("%d%d%d",&n,&m,&k) && (n||m||k)) {
25         if (SG[n]^SG[m]^SG[k]) printf("Fibo\n");  //多堆游戏XOR和 
26         else printf("Nacci\n");
27     }
28     return 0;
29 }
View Code

 HDU-1536

SG函数直接应用。要注意这题输入的操作集合不是升序的要先排序才行。

#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
int k,n,m,f[110],S[N],SG[N]; void getSG(int n) {memset(SG,0,sizeof(SG));memset(S,-1,sizeof(S));for (int i=1;i<=n;i++) {for (int j=1;j<=k&&f[j]<=i;j++) S[SG[i-f[j]]]=i;  for (int j=0;;j++) if (S[j]!=i) {  SG[i]=j; break;}}
}int main()
{while (scanf("%d",&k) && k) {for (int i=1;i<=k;i++) scanf("%d",&f[i]);sort(f+1,f+k+1);getSG(10000);scanf("%d",&n);for (int i=1;i<=n;i++) {int m,tmp=0; scanf("%d",&m);for (int j=1;j<=m;j++) {int x; scanf("%d",&x);tmp^=SG[x];}if (tmp==0) printf("L"); else printf("W"); }puts("");}return 0;
}
View Code

HDU-3980

这道题会难一些。首先原来是一个环不好处理,但是我们注意到只要第一步下手后就会变成链,所以我们一开始从n-m这条链开始求SG函数。那么假设此时是一条长度为n的链,我们在当前选手在上面选择了长度为m的区间染色,之后这条链就会分割成两段分别是i和n-i-m的后继状态,于是此时就会变成两个游戏,然后根据多堆Nim博弈异或和的结论,后继状态的SG函数就是SG[i]^SG[n-i-m],所以我们就能标记所有后继的SG函数得到当前状态的SG函数。

#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
int n,m,SG[N],S[N];int getSG(int n) {if (n<m) return 0;if (SG[n]!=-1) return SG[n];for (int i=0;n-i-m>=0;i++) S[getSG(i)^getSG(n-i-m)]=n;  //Nim博弈 for (int i=0;;i++)if (S[i]!=n) {SG[n]=i; break;} return SG[n];    
}int main()
{int T,Case=0; cin>>T;while (T--) {scanf("%d%d",&n,&m);memset(SG,-1,sizeof(SG));memset(S,-1,sizeof(-1));SG[0]=0;if (n<m || getSG(n-m)) printf("Case #%d: abcdxyzk\n",++Case);else printf("Case #%d: aekdycoin\n",++Case);}return 0;
}
View Code

 POJ-2505

SG函数应该很容易求。但是此题的数字非常大数组决定存不下,可以考虑用map计算SG函数可以获得AC。

#include<cstdio>
#include<iostream>
#include<map>
using namespace std;
typedef long long LL;
LL n;
map<LL,LL> SG;LL getSG(LL x) {if (x>=n) return 0;if (SG.count(x)) return SG[x];map<LL,int> S;for (int i=2;i<=9;i++) S[getSG(x*i)]=1;for (int i=0;;i++)if (!S.count(i)) return SG[x]=i;
}int main()
{while (scanf("%lld",&n)==1) {SG.clear();if (getSG(1)) printf("Stan wins.\n"); else printf("Ollie wins.\n");}return 0;
}
View Code

 

其他题目练习:

POJ-2484

很经典的一道题。给n个连成环的硬币,每次每人可以取一个或者相邻的两个硬币,最后取不到硬币的人输掉。这题要运用一种平均分然后不断模仿对方决策得到必胜的思想。首先如果硬币个数<=2先手必胜,大于3的时候第一个人取完之后环会变成一条链,然后无论第一个人取的是一个还是两个第二个人都可以根据第一个人取的数量和位置做出“把剩下硬币取成完全相同的两条链”(这里要重点理解)。然后通过模仿对方达到不败之地。所以硬币数量>=3时候先手必败。

#include<iostream>
#include<cstdio>
using namespace std;
const int N=1e6+10;
int n,m;int main()
{while (scanf("%d",&n)==1 && n) {if (n<=2) puts("Alice");else puts("Bob");}return 0;
}
View Code

POJ-2425

图上的博弈:一个n个点的有向无环图,有某些点有硬币,每次没人只可以把一个棋子往前推一步(当然是沿着边的方向推),最后不能推的人输掉。尽管是在DAG上的博弈,但是SG函数的求法还是那样,标记所有后继状态之后mex。然后有几个棋子就是XOR和即可。

#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
using namespace std;
const int N=1e3+10;
int n,m,q,SG[N];
vector<int> G[N];int getSG(int x) {if (SG[x]!=-1) return SG[x];int S[N]={0};for (int i=0;i<G[x].size();i++) {int y=G[x][i];S[getSG(y)]=1;}for (int i=0;;i++)if (S[i]!=1) return SG[x]=i;
}int main()
{while (scanf("%d",&n)==1) {for (int i=1;i<=n;i++) {G[i].clear();int t; scanf("%d",&t);for (int j=1;j<=t;j++) {int x; scanf("%d",&x); x++;G[i].push_back(x);}}memset(SG,-1,sizeof(SG));while (scanf("%d",&q) && q) {int ans=0;for (int j=1;j<=q;j++) {int x; scanf("%d",&x); x++;ans^=getSG(x);}printf("%s\n",ans?"WIN":"LOSE");}}return 0;
}
View Code

POJ 1704 Georgia and Bob

一列格子上有n个棋子,每次没人可以选择一个棋子往左移动若干步,棋子不能越过1格子,棋子也不能越过其他棋子移动,不能移动的人输掉。这题真的挺巧妙的,听大佬说这是一种叫阶梯博弈的博弈模型。我们先考虑对于一组相邻的棋子,这种情况肯定是先手必败,因为先手移动x格同时后手可以用后面那个棋子移动相同格。那么我们把棋子两两配对,那么两两配对的时候前面棋子的移动其实是没有意义的,因为与其配对的后面棋子可以做一模一样的操作。所以唯一的变数变成了两两配对的一组棋子间的间隙。

所以解法就是把棋子两两匹配,奇数时候第一个棋子和格子1匹配,然后对于每一组格子间的间隙当成一堆Nim游戏,求出所有Nim游戏的和就是答案。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1e3+10;
int n,a[N];int main()
{int T; cin>>T;while (T--) {scanf("%d",&n);for (int i=1;i<=n;i++) scanf("%d",&a[i]);sort(a+1,a+n+1);int ans=0;for (int i=n;i>0;i-=2) ans^=a[i]-a[i-1]-1;printf("%s\n",ans?"Georgia will win":"Bob will win");}return 0;
}
View Code

 


POJ 1740 A New Stone Game
POJ 2068 Nim
POJ 3480 John
POJ 2348 Euclid's Game
HOJ 2645 WNim
POJ 3710 Christmas Game 
POJ 3533 Light Switching Game

HOJ 4388 Stone Game II

ZJU 3057 beans game

转载于:https://www.cnblogs.com/clno1/p/11231919.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/247380.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Promise链式调用和解决回调地狱的ES7终极解决方法async,await

promise链式调用 **then 是成功回调&#xff0c;只要在then后边return一个promise就可以继续then**<script type"text/javascript">let p1new Promise(function(resolve,reject){setTimeout(function(){resolve()//成功回调// reject()//失败回调},2000)//2秒…

1.MySQL目录结构

分为两个目录&#xff1a; 1.安装目录&#xff1a; 2.数据目录&#xff1a; 在Linux下 yum安装mysql后使用 find / -name my.cnf 找到mysql数据文件的位置 转载于:https://www.cnblogs.com/sdrbg/p/11237231.html

对promise.all底层的实现的研究

1.Promise.all(iterable)返回一个新的Promise实例,此实例在iterable参数内素有的Promise都fulfilled或者参数中不包含Promise时&#xff0c;状态变成fulfilled。 如果参数中Promise有一个失败rejected &#xff0c;此实例回调失败&#xff0c;失败原因的是第一个失败Promise的返…

vue-provide/inject轻松实现跨级访问祖先组件

provide&#xff1a;Object | () > Object inject&#xff1a;Array<string> | { [key: string]: string | Symbol | Object } provide 和 inject 主要为高阶插件/组件库提供用例。并不推荐直接用于应用程序代码中。是2.2.0版本 新增的。 这对选项需要一起使用&#x…

python 操作redis,存取为字节格式,避免转码加

这种情况连接数据库&#xff0c;对数据的存取都是字节类型&#xff0c;存取时还得转码一下 from redis import Redis# 实例化redis对象rdb Redis(hostlocalhost, port6379, db0,passwordaaa123) rdb.set(name, root) name rdb.get(name) print(name)为了避免上述情况&#x…

element ui table scrollTop 滚动到行头或行尾

1.设置table的ref为tableList2.设置滚动至顶部this.$refs.tableList.bodyWrapper.scrollTop 0;3.设置滚动至底部this.$refs.tableList.bodyWrapper.scrollTop this.$refs.tableList.bodyWrapper.scrollHeight;//如果请求完更新数据&#xff0c;需要使用$nextTick this.$nextTic…

Element-UI中Drawer抽屉去除标题自带蓝色框

当点击事件drawertrue时&#xff0c;抽匣回打开 这时抽匣的标题会出现一个难看的蓝色边框&#xff0c;一会就会消失&#xff0c;但是好丑&#xff0c;所以要去掉它 解决方法 /deep/ :focus {outline: 0;} vue组件中&#xff0c;在style设置为scoped的时候&#xff0c;里面在…

Java生鲜电商平台-B2B生鲜的互联网思维

Java生鲜电商平台-B2B生鲜的互联网思维 在互联网高速发展的今天&#xff0c;为我们的生活带来了众多便利。然而互联网从早期的萌芽状态到现在妇孺皆知&#xff0c;它的崛起速度远远超乎世人的想象。人们开始关注互联网并且研究它&#xff0c;而思维模式的研究则是从深层次研究事…

Java生鲜电商平台-高并发核心技术订单与库存实战

Java生鲜电商平台-高并发核心技术订单与库存实战 一、 问题 一件商品只有100个库存&#xff0c;现在有1000或者更多的用户来购买&#xff0c;每个用户计划同时购买1个到几个不等商品。 如何保证库存在高并发的场景下是安全的&#xff1f; &#xff08;1&#xff09;不多发 &…

Vue2 MVVM 双向绑定(数据劫持+发布者-订阅者模式)

参考文献&#xff1a;https://www.cnblogs.com/libin-1/p/6893712.html https://juejin.im/post/5b2f0769e51d45589f46949e MVVM拆开来即为Model-View-ViewModel&#xff0c;有View&#xff0c;ViewModel&#xff0c;Model三部分组成。View层代表的是视图、模版&#xff0c;负…

常用的激活函数

1.阶跃函数 &#xff0c;值域{0,1} 1 def step_function(x): 2 return np.array(x>0,dtypenp.int) 2.sigmoid函数 &#xff0c;值域(0,1) 1 def sigmoid(x): 2 return 1/(1np.exp(-x)) 3.relu函数 &#xff0c;值域[0&#xff0c;∞&#xff09; 1 def relu(x): 2 …

前端优化-vue-cli4安装webpack-bundle-analyzer分析包文件

使用vue-cli3创建了一个工程目录&#xff0c;技术栈为vue-cli3vue-routervuexelement-uiv-chartsaxios。但是等到项目开发完后&#xff0c;发现生成的app.js特别大&#xff0c;接近10M。为了优化项目性能&#xff0c;需要使用webpack-bundle-analyzer分析包文件&#xff0c;找出…

今天刚查到的宏,学习

PVP常用的宏&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&…

不要再问我三次握手和四次挥手

三次握手和四次挥手是各个公司常见的考点&#xff0c;也具有一定的水平区分度&#xff0c;也被一些面试官作为热身题。很多小伙伴说这个问题刚开始回答的挺好&#xff0c;但是后面越回答越冒冷汗&#xff0c;最后就歇菜了。 见过比较典型的面试场景是这样的: 面试官&#xff1…

Apache ServiceComb

Apache ServiceComb 开源&#xff0c;全栈微服务解决方案。开箱即用&#xff0c;高性能&#xff0c;兼容流行的生态&#xff0c;多语言支持 ServiceComb是一个微服务框架&#xff0c;提供服务注册&#xff0c;发现&#xff0c;配置和管理实用程序。 下载 &#xff1a;http://se…

VScode PowerShell运行脚本报错禁止运行脚本解决方式图文

今天在新Windows电脑上用VScode的终端PowerShell运行一个脚本的时候&#xff0c; 错误 在vscode终端运行vue -V查看版本失败 PS C:\Users\11388> vue -V vue : 无法加载文件 E:\NodeJs\node_global\vue.ps1&#xff0c;因为在此系统上禁止运行脚本。有关详细信息&#xf…

多线程的创建方式---继承Thread和实现Runnable

继承Thread类创建多线程 1 package cn.ftf.thread;2 /**3 * 多线程实现方式一 继承Thread实现多线程&#xff0c;继承Thread&#xff0c;重写run方法4 * author 房廷飞5 *6 */7 public class StartThread extends Thread{ //对象继承Thread8 public static void mai…

添加右键用Sublime Text3 打开文件和文件夹

最近重新装了一下系统&#xff0c;右键没有用Sublime Text 3打开的选项了&#xff0c;于是查了一下解决方案 1、环境 Win10和Win7都可以Sublime Text 3最新版本以下为Win10系统下截图 2、添加右键打开文件 Win R&#xff0c;输入regedit,打开注册表 找到HKEY_CLASSESS_ROOT…

Windows Mobile Widget Emulator

今天Vimpyboy 在codeplex发布了Windows Mobile Widget Emulator。这是一个用来调试Windows Mobile 6.5 Widget的工具&#xff0c;我在做Windows Mobile 6.5 新功能widget开发 的时候就发现调试Widget很麻烦。也有想法做一个Emulator&#xff0c;其实这个Emulator目标很明显&…

JS 常用字符串数组遍历函数方法整理

目录 一、concat() 二、join() 三、push() 四、pop() 五、shift() 六、unshift() 七、slice() 九、substring() 和 substr() 十、sort 排序 十一、reverse() 十二、indexOf 和 lastIndexOf 十三、every 十四、some 十五、filter 十六、map ES6新增新操作数组的…