博弈论入门(Game Theory)

2024-02-08 15:59
文章标签 入门 theory 博弈论 game

本文主要是介绍博弈论入门(Game Theory),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

博弈论(Game Theory)

      • 一、巴什博弈(Bash Game)
        • 操作:
        • 代码:
        • 例题:
          • 1.Brave Game
          • 2. kiki's game
      • 二、威佐夫博弈(Wythoff Game)
        • 操作:
        • 代码:
        • 例题:
          • 1.取石子游戏
      • 三、尼姆博弈论(Nim Game)
        • 操作:
        • 代码:
        • 例题:
          • 1. Being a Good Boy in Spring Festival
      • 四、斐波那契博弈论
        • 操作:
        • 代码:
        • 例题:
          • 1. 取石子游戏
      • 五、SG函数
        • 操作:
        • 代码:
        • 例题:
          • 1. Good Luck in CET-4 Everybody!
          • 2. S-Nim

一、巴什博弈(Bash Game)

一堆n个物品,两人从中轮流取出(1~m)个;最后取光者胜。

操作:

n=k * (m+1)+r
先手拿走r个,那么后手无论拿走几个(1~m),先手只要使得拿走的和为m+1,先手就赢。
反之,n=k * (m+1)
先手无论怎么操作都会输

代码:
int main(){LL t;sf(t);while(t--){LL n,m;sf(n),sf(m);if(n%(m+1))printf("first\n");//可以把k*(m+1)这个状态给对面else printf("second\n");//到自己手上的状态是k*(m+1)}
}
例题:
1.Brave Game

HDU - 1846
十年前读大学的时候,中国每年都要从国外引进一些电影大片,其中有一部电影就叫《勇敢者的游戏》(英文名称:Zathura),一直到现在,我依然对于电影中的部分电脑特技印象深刻。
今天,大家选择上机考试,就是一种勇敢(brave)的选择;这个短学期,我们讲的是博弈(game)专题;所以,大家现在玩的也是“勇敢者的游戏”,这也是我命名这个题目的原因。
当然,除了“勇敢”,我还希望看到“诚信”,无论考试成绩如何,希望看到的都是一个真实的结果,我也相信大家一定能做到的~

各位勇敢者要玩的第一个游戏是什么呢?很简单,它是这样定义的:
1、 本游戏是一个二人游戏;
2、 有一堆石子一共有n个;
3、 两人轮流进行;
4、 每走一步可以取走1…m个石子;
5、 最先取光石子的一方为胜;

如果游戏的双方使用的都是最优策略,请输出哪个人能赢。
Input
输入数据首先包含一个正整数C(C<=100),表示有C组测试数据。
每组测试数据占一行,包含两个整数n和m(1<=n,m<=1000),n和m的含义见题目描述。
Output
如果先走的人能赢,请输出“first”,否则请输出“second”,每个实例的输出占一行。
Sample Input
2
23 2
4 3
Sample Output
first
second

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
#include<string>
using namespace std;int main()
{int T,n,m;cin>>T;while(T--){cin>>n>>m;int l=n%(m+1);if(l) cout<<"first"<<endl;else cout<<"second"<<endl;}
}
2. kiki’s game

HDU - 2147
Recently kiki has nothing to do. While she is bored, an idea appears in his mind, she just playes the checkerboard game.The size of the chesserboard is n*m.First of all, a coin is placed in the top right corner(1,m). Each time one people can move the coin into the left, the underneath or the left-underneath blank space.The person who can’t make a move will lose the game. kiki plays it with ZZ.The game always starts with kiki. If both play perfectly, who will win the game?
Input
Input contains multiple test cases. Each line contains two integer n, m (0<n,m<=2000). The input is terminated when n=0 and m=0.
Output
If kiki wins the game printf “Wonderful!”, else “What a pity!”.

Sample Input
5 3
5 4
6 6
0 0
Sample Output
What a pity!
Wonderful!
Wonderful!

Tips: 只要m或者n有一个是偶数先手就能必胜。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
#include<string>
using namespace std;int main( )
{int n,m;while(~scanf("%d%d",&n,&m)){if(m==0||n==0) break;if(n%2==0||m%2==0)printf("Wonderful!\n");elseprintf("What a pity!\n");}
}

二、威佐夫博弈(Wythoff Game)

有两堆各若干个物品,两个人轮流从某一堆取出至少一个或同时从两堆中取同样多的物品,规定每次至少取一个,多者不限,最后取光者得胜。

操作:

求出差值,如果 差值*黄金分割==最小值 后手赢,否则先手赢。

代码:
int main(){int a,b;while(scanf("%d%d",&a,&b)!=EOF){if(a>b)swap(a,b);int k=b-a;int ans=(a!=floor(k*(1.0+sqrt(5.0))/2));printf("%d\n",ans);//0必败态 1必胜态}return 0;
}
例题:
1.取石子游戏

HDU - 1527
有两堆石子,数量任意,可以不同。游戏开始由两个人轮流取石子。游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子;二是可以在两堆中同时取走相同数量的石子。最后把石子全部取完者为胜者。现在给出初始的两堆石子的数目,如果轮到你先取,假设双方都采取最好的策略,问最后你是胜者还是败者。

Input
输入包含若干行,表示若干种石子的初始情况,其中每一行包含两个非负整数a和b,表示两堆石子的数目,a和b都不大于1,000,000,000。
Output
输出对应也有若干行,每行包含一个数字1或0,如果最后你是胜者,则为1,反之,则为0。

Sample Input
2 1
8 4
4 7
Sample Output
0
1
0

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
#include<string>
using namespace std;int main()
{int n,m;while(~scanf("%d%d",&n,&m)){if(n>m) swap(n,m);int l=(m-n)*(1.0*(sqrt(5)+1)/2);if(n==l) printf("0\n");else printf("1\n");}
}

三、尼姆博弈论(Nim Game)

巴什博弈论扩展到多堆石头就是尼姆博弈论。
有若干堆石子,每堆石子的数量都是有限的,合法的移动是“选择一堆石子并拿走若干颗(不能不拿)”,如果轮到某个人时所有的石子堆都已经被拿空了,则判负。

操作:

a1⊕a2⊕a3⊕…⊕ak ==0,先手胜。
a1⊕a2⊕a3⊕…⊕ak !=0,先手败。

异或运算的结果不等于0,先手必胜。

在数学中,二进制的异或运算也可以看成是统计每一位上1的总个数的奇偶性:这一位上有偶数个1,那么这一位的计算结果为0;如果有奇数个,计算结果为1.所以,尼姆游戏中的异或运算也被称为Nim-sum运算。

下面对操作做简单的证明:
.
(1)必定能够从N- position转化到P-position.也就是说,先手处于必胜点N-position时可以拿走一一些石子,让后手必败。
读者可以先自己思考如何转化。下面是具体方法:任选一堆,例如第i堆,石头数量是k;对剩下的n-1堆做异或运算,设结果为H;如果H比k小,就把第i堆石头减少到H;这样操作之后,因为H^H=Q,所以n堆石头的异或等于0。可以证明,总会存在这样的第i堆石头,而且可能有多种转化方案。
下面例题hdu1850的程序中的“if((sum^a[i])<=a[i])”统计了所有方案。
.
(2)进人P-position后,轮到的下一个玩家,不管拿多少石子都会转移到N-position,因为任何一堆的数量变化,都会使得这堆的进制数至少有一位发生变化,导致异或运算的结果不等于0。也就是说,这一个玩家不管怎么拿石子都必败。

.
(3)在游戏过程中,按上述(1)和(2)的步骤在Nposition和P-position之间交替转化,直到所有堆的石头都是0,即终止于P-position。

上述证明过程也说明了玩家该如何进行游戏。
——《算法竞赛入门到进阶》

代码:
int main(){int m,ans,n;while(~scanf("%d",&m)){n=ans=0;while(m--)scanf("%d",&n),ans^=n,printf("ans=%d\n",ans);if(ans)printf("Yes\n");else printf("No\n");}
}
例题:
1. Being a Good Boy in Spring Festival

HDU - 1850
一年在外 父母时刻牵挂
春节回家 你能做几天好孩子吗
寒假里尝试做做下面的事情吧

陪妈妈逛一次菜场
悄悄给爸爸买个小礼物
主动地 强烈地 要求洗一次碗
某一天早起 给爸妈用心地做回早餐

如果愿意 你还可以和爸妈说
咱们玩个小游戏吧 ACM课上学的呢~

下面是一个二人小游戏:桌子上有M堆扑克牌;每堆牌的数量分别为Ni(i=1…M);两人轮流进行;每走一步可以任意选择一堆并取走其中的任意张牌;桌子上的扑克全部取光,则游戏结束;最后一次取牌的人为胜者。
现在我们不想研究到底先手为胜还是为负,我只想问大家:
——“先手的人如果想赢,第一步有几种选择呢?”
Input
输入数据包含多个测试用例,每个测试用例占2行,首先一行包含一个整数M(1<M<=100),表示扑克牌的堆数,紧接着一行包含M个整数Ni(1<=Ni<=1000000,i=1…M),分别表示M堆扑克的数量。M为0则表示输入数据的结束。
Output
如果先手的人能赢,请输出他第一步可行的方案数,否则请输出0,每个实例的输出占一行。
Sample Input
3
5 7 9
0
Sample Output
1

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
#include<string>
using namespace std;const int N = 100;
int a[N];int main()
{int n;while(~scanf("%d", &n) && n){int ans = 0;for(int i=0;i<n;i++){scanf("%d",&a[i]);ans^=a[i];}if(ans==0)printf("0\n");else {int cnt=0;for(int i=0;i<n;i++)if((ans^a[i])<a[i]) cnt++;printf("%d\n",cnt);}}
}

四、斐波那契博弈论

有一堆个数为n的石子,游戏双方轮流取石子,满足:
1)先手不能在第一次把所有的石子取完;
2)之后每次可以取的石子数介于1到对手刚取的石子数的2倍之间(包含1和对手刚取的石子数的2倍)。
约定取走最后一个石子的人为赢家,求必败态。

操作:

当n为Fibonacci数时,先手必败。
否则先手必胜。

代码:
int f[1000];
int F(int n)
{f[1]=1;f[2]=1;for(int i=3;i<=100;i++){f[i] = f[i-1]+f[i-2];if(f[i]==n)return 1;if(f[i]>n)break;}return 0;
}
例题:
1. 取石子游戏

HDU - 2516
1堆石子有n个,两人轮流取.先取者第1次可以取任意多个,但不能全部取完.以后每次取的石子数不能超过上次取子数的2倍。取完者胜.先取者负输出"Second win".先取者胜输出"First win".
Input
输入有多组.每组第1行是2<=n<2^31. n=0退出.
Output
先取者负输出"Second win". 先取者胜输出"First win".
参看Sample Output.
Sample Input
2
13
10000
0
Sample Output
Second win
Second win
First win

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
#include<string>
using namespace std;int f[1000];
int F(int n)
{f[1]=1;f[2]=1;for(int i=3;i<=100;i++){f[i] = f[i-1]+f[i-2];if(f[i]==n)return 1;if(f[i]>n)break;}return 0;
}
int main()
{int n;while(cin>>n){if(n==0)break;if(F(n)==0)printf("First win\n");elseprintf("Second win\n");}
}

五、SG函数

先定义mex(minimal excludant)运算,这是施加于一个集合的运算,表示最小的不属于这个集合的非负整数。
例如mex{0,1,2,4}=3、mex{2,3,5}=0、mex{}=0。

操作:

求出sg(x),为等于没有指定给它的任意后继结点的sg值得最小非负整数。
《算法竞赛从入门到进阶》

代码:
void getSG(int n){memset(sg,0,sizeof sg);for(int i=1;i<=n;i++){               //n个物品memset(vis,0,sizeof vis);for(int j=0;j<=m&&i-j>0;j++)vis[sg[i-j]]=1;              //j表示可以取的数for(int j=0;j<=n;j++)if(!vis[j]){sg[i]=j;break;}}
}
例题:
1. Good Luck in CET-4 Everybody!

HDU - 1847
大学英语四级考试就要来临了,你是不是在紧张的复习?也许紧张得连短学期的ACM都没工夫练习了,反正我知道的Kiki和Cici都是如此。当然,作为在考场浸润了十几载的当代大学生,Kiki和Cici更懂得考前的放松,所谓“张弛有道”就是这个意思。这不,Kiki和Cici在每天晚上休息之前都要玩一会儿扑克牌以放松神经。
“升级”?“双扣”?“红五”?还是“斗地主”?
当然都不是!那多俗啊~
作为计算机学院的学生,Kiki和Cici打牌的时候可没忘记专业,她们打牌的规则是这样的:
1、 总共n张牌;
2、 双方轮流抓牌;
3、 每人每次抓牌的个数只能是2的幂次(即:1,2,4,8,16…)
4、 抓完牌,胜负结果也出来了:最后抓完牌的人为胜者;
假设Kiki和Cici都是足够聪明(其实不用假设,哪有不聪明的学生~),并且每次都是Kiki先抓牌,请问谁能赢呢?
当然,打牌无论谁赢都问题不大,重要的是马上到来的CET-4能有好的状态。

Good luck in CET-4 everybody!
Input
输入数据包含多个测试用例,每个测试用例占一行,包含一个整数n(1<=n<=1000)。
Output
如果Kiki能赢的话,请输出“Kiki”,否则请输出“Cici”,每个实例的输出占一行。
Sample Input
1
3
Sample Output
Kiki
Cici

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
#include<string>
using namespace std;const int N=1010;
int sg[N],f[11];
bool vis[N];void getSG(int n)
{memset(sg,0,sizeof sg);for(int i=1;i<=n;i++){memset(vis,0,sizeof vis);for(int j=0;f[j]<=i;j++)vis[sg[i-f[j]]]=1;for(int j=0;j<=n;j++)if(!vis[j]){sg[i]=j;break;}}
}
int main()
{int n;for(int i=0;i<=10;i++) f[i]=(1<<i);getSG(1000);while(~scanf("%d",&n)&&n){if(sg[n]) printf("Kiki\n");else printf("Cici\n");}
}
2. S-Nim

POJ - 2960
Arthur and his sister Caroll have been playing a game called Nim for some time now. Nim is played as follows:
The starting position has a number of heaps, all containing some, not necessarily equal, number of beads.
The players take turns chosing a heap and removing a positive number of beads from it.
The first player not able to make a move, loses.
Arthur and Caroll really enjoyed playing this simple game until they
recently learned an easy way to always be able to find the best move:
Xor the number of beads in the heaps in the current position (i.e. if we have 2, 4 and 7 the xor-sum will be 1 as 2 xor 4 xor 7 = 1).
If the xor-sum is 0, too bad, you will lose.
Otherwise, move such that the xor-sum becomes 0. This is always possible.
It is quite easy to convince oneself that this works. Consider these facts:
The player that takes the last bead wins.
After the winning player’s last move the xor-sum will be 0.
The xor-sum will change after every move.
Which means that if you make sure that the xor-sum always is 0 when you have made your move, your opponent will never be able to win, and, thus, you will win.

Understandibly it is no fun to play a game when both players know how to play perfectly (ignorance is bliss). Fourtunately, Arthur and Caroll soon came up with a similar game, S-Nim, that seemed to solve this problem. Each player is now only allowed to remove a number of beads in some predefined set S, e.g. if we have S = {2, 5} each player is only allowed to remove 2 or 5 beads. Now it is not always possible to make the xor-sum 0 and, thus, the strategy above is useless. Or is it?

your job is to write a program that determines if a position of S-Nim is a losing or a winning position. A position is a winning position if there is at least one move to a losing position. A position is a losing position if there are no moves to a losing position. This means, as expected, that a position with no legal moves is a losing position.
Input
Input consists of a number of test cases.
For each test case: The first line contains a number k (0 < k ≤ 100) describing the size of S, followed by k numbers si (0 < si ≤ 10000) describing S. The second line contains a number m (0 < m ≤ 100) describing the number of positions to evaluate. The next m lines each contain a number l (0 < l ≤ 100) describing the number of heaps and l numbers hi (0 ≤ hi ≤ 10000) describing the number of beads in the heaps.
The last test case is followed by a 0 on a line of its own.
Output
For each position: If the described position is a winning position print a ‘W’.If the described position is a losing position print an ‘L’.
Print a newline after each test case.
Sample Input
2 2 5
3
2 5 12
3 2 4 7
4 2 3 7 12
5 1 2 3 4 5
3
2 5 12
3 2 4 7
4 2 3 7 12
0
Sample Output
LWW
WWL

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
#include<string>
using namespace std;
int s[105],sg[10001];
bool vis[10001];
void getSG(int t)
{int i,j;memset(sg,0,sizeof sg );for(i=1;i<=10001;i++){memset(vis,0,sizeof vis );for(j=1;j<=t&&s[j]<=i;j++)vis[sg[i-s[j]]]=1;for(j=0;j<=10001;j++)if(!vis[j])break;sg[i]=j;}
}
int main()
{int k;while(cin>>k,k){for(int i=1;i<=k;i++)cin>>s[i];sort(s+1,s+k+1);getSG(k);int m,n,ans,t;cin>>m;while(m--){cin>>n;ans=0;for(int i=0;i<n;i++){cin>>t;ans^=sg[t];}if(ans)cout<<'W';elsecout<<'L';}cout<<endl;}
}

参考资料:
https://blog.csdn.net/haha294182852/article/details/77572639
https://blog.csdn.net/jk_chen_acmer/article/details/82082653
https://blog.csdn.net/bestsort/article/details/88197959
https://www.bilibili.com/video/BV1Ut4y1X7ry?from=search&seid=2134696235016320646
https://www.bilibili.com/video/BV15b411W73J?from=search&seid=11761397602115561012
↓ 觉得还ok,留下一个赞吧~ 蟹蟹~

这篇关于博弈论入门(Game Theory)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/691522

相关文章

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

数论入门整理(updating)

一、gcd lcm 基础中的基础,一般用来处理计算第一步什么的,分数化简之类。 LL gcd(LL a, LL b) { return b ? gcd(b, a % b) : a; } <pre name="code" class="cpp">LL lcm(LL a, LL b){LL c = gcd(a, b);return a / c * b;} 例题:

Java 创建图形用户界面(GUI)入门指南(Swing库 JFrame 类)概述

概述 基本概念 Java Swing 的架构 Java Swing 是一个为 Java 设计的 GUI 工具包,是 JAVA 基础类的一部分,基于 Java AWT 构建,提供了一系列轻量级、可定制的图形用户界面(GUI)组件。 与 AWT 相比,Swing 提供了许多比 AWT 更好的屏幕显示元素,更加灵活和可定制,具有更好的跨平台性能。 组件和容器 Java Swing 提供了许多

【IPV6从入门到起飞】5-1 IPV6+Home Assistant(搭建基本环境)

【IPV6从入门到起飞】5-1 IPV6+Home Assistant #搭建基本环境 1 背景2 docker下载 hass3 创建容器4 浏览器访问 hass5 手机APP远程访问hass6 更多玩法 1 背景 既然电脑可以IPV6入站,手机流量可以访问IPV6网络的服务,为什么不在电脑搭建Home Assistant(hass),来控制你的设备呢?@智能家居 @万物互联

poj 2104 and hdu 2665 划分树模板入门题

题意: 给一个数组n(1e5)个数,给一个范围(fr, to, k),求这个范围中第k大的数。 解析: 划分树入门。 bing神的模板。 坑爹的地方是把-l 看成了-1........ 一直re。 代码: poj 2104: #include <iostream>#include <cstdio>#include <cstdlib>#include <al

MySQL-CRUD入门1

文章目录 认识配置文件client节点mysql节点mysqld节点 数据的添加(Create)添加一行数据添加多行数据两种添加数据的效率对比 数据的查询(Retrieve)全列查询指定列查询查询中带有表达式关于字面量关于as重命名 临时表引入distinct去重order by 排序关于NULL 认识配置文件 在我们的MySQL服务安装好了之后, 会有一个配置文件, 也就

fzu 2275 Game KMP

Problem 2275 Game Time Limit: 1000 mSec    Memory Limit : 262144 KB  Problem Description Alice and Bob is playing a game. Each of them has a number. Alice’s number is A, and Bob’s number i

音视频入门基础:WAV专题(10)——FFmpeg源码中计算WAV音频文件每个packet的pts、dts的实现

一、引言 从文章《音视频入门基础:WAV专题(6)——通过FFprobe显示WAV音频文件每个数据包的信息》中我们可以知道,通过FFprobe命令可以打印WAV音频文件每个packet(也称为数据包或多媒体包)的信息,这些信息包含该packet的pts、dts: 打印出来的“pts”实际是AVPacket结构体中的成员变量pts,是以AVStream->time_base为单位的显

C语言指针入门 《C语言非常道》

C语言指针入门 《C语言非常道》 作为一个程序员,我接触 C 语言有十年了。有的朋友让我推荐 C 语言的参考书,我不敢乱推荐,尤其是国内作者写的书,往往七拼八凑,漏洞百出。 但是,李忠老师的《C语言非常道》值得一读。对了,李老师有个官网,网址是: 李忠老师官网 最棒的是,有配套的教学视频,可以试看。 试看点这里 接下来言归正传,讲解指针。以下内容很多都参考了李忠老师的《C语言非

MySQL入门到精通

一、创建数据库 CREATE DATABASE 数据库名称; 如果数据库存在,则会提示报错。 二、选择数据库 USE 数据库名称; 三、创建数据表 CREATE TABLE 数据表名称; 四、MySQL数据类型 MySQL支持多种类型,大致可以分为三类:数值、日期/时间和字符串类型 4.1 数值类型 数值类型 类型大小用途INT4Bytes整数值FLOAT4By