初学 博弈论 又称对策论 Game Theory
Posted 163467wyj
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了初学 博弈论 又称对策论 Game Theory相关的知识,希望对你有一定的参考价值。
博弈论 真的很有趣 ,回想起 前两天多校一道题的题解 所有不公平的游戏都存在必胜的玩法
与人斗其乐无穷 https://vjudge.net/contest/241983#overview 博弈论专题
一)巴什博奕(Bash Game):有n个石头,Alice和Bob轮流取石头,每次取的石头不能超过m个,Alice开始取,最后取完的赢,两个人都是以最优的方案取,求最终赢的是谁
假设 n=m+1个,Alice 先取,无论她取多少个(>=1),Bob 都能一次性把剩下的取完,so ? Alice必输
情况就可以推广变成 如果 n= (m+1)*k 也就是 n%(m+1)==0 时,Alice 必输 ,反之 Alice 必赢
代码的话 实现很简单 hdu2188 hdu2149
1 #include<cstdio> 2 int main(){ 3 int t,n,m; 4 scanf("%d",&t);while(t--){ 5 scanf("%d%d",&n,&m); 6 if(n%(m+1)==0)printf("Bob "); 7 else printf("Alice "); 8 } 9 return 0; 10 }
二)威佐夫博奕(Wythoff Game):有两堆石头,Alice和Bob轮流从某一堆取任意多的石头,或者从两堆中取相同多的石头,每次至少取一个石头, Alice开始取,最后取完的赢,两个人都是以最优的方案取,求最终赢的是谁(hdu 1846 hdu1527 51NOd1185)
就首先引入奇异局势,也就是当面临其一局势的状态必输。 比如(0,0)就是一个奇异局势,面临这种情况必输。 接下来的我就不懂了。。。。- = .= 十分钟前 ,我现在模拟了几组,懂了
前几个奇异局势有(0,0)、(1,2)、(3,5)、(4,7)、(6,10)、(8,13)、(9,15)、(11,18)、(12,20) ...
性质就是每个自然数都会出现在一个且唯一的一个奇异局势里面 ,非奇异局势可以经过适当操作转化为奇异局势
两个数 不是奇异局势 就是 非奇异局势 ,当Alice面对奇异局势时,必输,反之,必赢
不是很懂的话 随便想两个数 模拟试试 多试几组就好了
三)尼姆博弈
有三堆石头,Alice和Bob轮流从其中一堆中取任意多的石头,每次至少取一个石头, Alice开始取,最后取完的赢,两个人都是以最优的方案取,求最终赢的是谁
奇异局势:第一个奇异局势是(0,0,0),面对这种情况的一定输。
设有a,b,c三堆石头,当a^b^c == 0时就是奇异局势,必输。
非奇异局势一定能变为奇异局势,奇异局势一定能变成非奇异局势。
证明:
当a^b^c != 0时,a+b+c != 0,即还有石头。
令a^b = x1,a^c = x2,b^c = x3,一定会存在至少下面的一种情况:
x1 < c,x2 < b,x3 < a。
假设x1 < c ,令c‘ =c – (c – x1) = x1,
得a^b^c’ == 0。
即非奇异局势变为奇异局势。
当a^b^c == 0 时,即a^b = c
只需要变成其中的任意一个数就可以当a^b != c。
即奇异局势变为非奇异局势。
由于(0,0,0)是最终得到的奇异局势,并且没有石头可取,所以面对a^b^c== 0的状态必输。
就异或为零和不为零情况就是了
http://acm.hdu.edu.cn/showproblem.php?pid=1907 多组输入 一个棋盘上黑白的位置 问谁嬴谁输 不懂为啥得 a-b-1 ???
走子方式,于是我们通过sg函数的定义,爆出sg函数值就发现sg函数值就是两个棋子的距离的绝对值减1.其实这个还是可以理解的,因为我们看当两个棋子相邻的时候,已经是一个必败态了。 ???
#include <cstdio> #include <cstdlib> #include <cmath> #include <algorithm> int main() { int n,m,a,b; while(scanf("%d%d",&n,&m)!=EOF){ int cnt =0,flag =0; for(int i=0;i< n;i++){ scanf("%d%d",&a,&b); if(a < b)std::swap(a,b); if(a > 1)flag++;cnt ^= abs(a-b-1);//??? } printf(cnt==0 ?"BAD LUCK! ":"I WIN! "); } return 0; }
SG函数
SG函数可以看成各个Nim游戏的和,将问题分成各种Nim问题,从而简化了问题。
例如:有三堆石头,每次能取的数量为斐波拉系数,即只能取1,2,3,5,8,13,21……这些石头的数量。
定义mex运算:表示最小的不属于这个集合的数。例如:mex{0,1,2,3} = 4、mex{0,1,3,4,5} = 2、mex{} = 0
对于任意的x,Sg(x) = mex{S},S为x的后继状态数值的集合,假设有三个状态Sg(a)、Sg(b)、Sg(c)。那么Sg(x) = mex{Sg(a),Sg(b),Sg(c)}。
设有三堆石头为x、y、z这三堆。那么只要判断Sg(x)^Sg(y)^Sg(z) == 0就行,当成立时就说明面对的是奇异局势,必输,反正必赢。
/* 摘自jumping_frog 博客
首先定义mex(minimal excludant)运算,这是施加于一个集合的运算,表示最小的不属于这个集合的非负整数。例如
mex{0,1,2,4}=3、mex{2,3,5}=0、mex{}=0。
对于一个给定的有向无环图,定义关于图的每个顶点的Sprague-Grundy函数g如下:g(x)=mex{ g(y) | y是x的后继},这里的g(x)即
sg[x]。
例如:取石子问题,有1堆n个的石子,每次只能取{1,3,4}个石子,先取完石子者胜利,那么各个数的SG值为多少?
sg[0]=0,f[]={1,3,4},
x=1时,可以取走1-f{1}个石子,剩余{0}个,mex{sg[0]}={0},故sg[1]=1;
x=2时,可以取走2-f{1}个石子,剩余{1}个,mex{sg[1]}={1},故sg[2]=0;
x=3时,可以取走3-f{1,3}个石子,剩余{2,0}个,mex{sg[2],sg[0]}={0,0},故sg[3]=1;
x=4时,可以取走4-f{1,3,4}个石子,剩余{3,1,0}个,mex{sg[3],sg[1],sg[0]}={1,1,0},故sg[4]=2;
x=5时,可以取走5-f{1,3,4}个石子,剩余{4,2,1}个,mex{sg[4],sg[2],sg[1]}={2,0,1},故sg[5]=3;
以此类推.....
x 0 1 2 3 4 5 6 7 8....
sg[x] 0 1 0 1 2 3 2 0 1....
计算从1-n范围内的SG值。
f(存储可以走的步数,f[0]表示可以有多少种走法)
f[]需要从小到大排序
1.可选步数为1~m的连续整数,直接取模即可,SG(x) = x % (m+1);
2.可选步数为任意步,SG(x) = x;
3.可选步数为一系列不连续的数,用getSG()计算
*/
不懂i SG函数 先放着 过段时间再来看 如果是费波纳茨序列的话 只需数组开16 第十六个费波纳茨数就1196
打表 和 dfs
// 获得SG数组函数模板,t代表f数组的个数,n代表要求的sg数组上限 // f数组就是能取的个数(对于此题就是Fibonacci数列 // 有时,对于t已知就不需要单独传参 void get_sg(int t,int n) { int i,j; memset(sg,0,sizeof(sg)); for(i=1;i<=n;i++) { memset(mex,0,sizeof(mex)); // 对于属于g(x)后继的数置1 for( j=1 ;j<=t && fib[j]<=i ;j++ ) mex[sg[i-fib[j]]]=1; // 找到最小不属于该集合的数 for( j=0 ; j<=n ; j++ ) if(!mex[j]) break; sg[i] = j; } }
//注意 S数组要按从小到大排序 SG函数要初始化为-1 对于每个集合只需初始化1遍 //n是集合s的大小 S[i]是定义的特殊取法规则的数组 int s[N],sg[N],n; bool vis[N]; int dfs_SG(int x){ if(sg[x] != -1) return sg[x]; memset(vis,0,sizeof(vis)); for(int i = 0; i < n; ++i){ if(x >= s[i]){ dfs_SG(x-s[i]); vis[sg[x-s[i]]] = 1; } } for(int i = 0;; ++i){ if(!vis[i]){ e = i; return sg[x] = i; } } }
#include <bits/stdc++.h> // hdu 1848 using namespace std; const int N = 1010; int sg[N], mex[N]; int fa[55]; void getSG(int n) { for(int i = 1; i <= n; i ++) { memset(mex, 0, sizeof(mex)); for(int j = 1; fa[j] <= i; ++ j) mex[sg[i-fa[j]]] = 1; for(int j = 0; ; j ++) { if(!mex[j]) { sg[i] = j; break; } } } } int main() { int a, b, c; fa[1] = 1, fa[2] = 2; for(int i = 3; i < 55; i ++) fa[i] = fa[i-1] + fa[i-2]; getSG(1000); while(cin >> a >> b >> c) { if(a==0 && b==0 && c==0)break; if(sg[a] ^ sg[b] ^ sg[c])printf("Fibo "); else printf("Nacci "); } return 0; }
#include <bits/stdc++.h> using namespace std; const int M = 105; int sg[M][M], vis[M], n, m; void fun() { memset(sg, 0, sizeof(sg)); for (int i = 1; i <= m; i++) for (int j = 1; j <= m; j++) { if (i == j) continue; memset(vis, 0, sizeof(vis)); if (i < j) { for (int k = i + 1; k < j; k++) vis[ sg[k][j] ] = 1, vis[ sg[i][k] ] = 1; } else if (i > j) { for (int k = j + 1; k < i; k++ ) vis[ sg[k][j] ] = 1, vis[ sg[i][k] ] = 1; } for (int k = 0;; k++) if (!vis[k]) { sg[i][j] = k; break; } } } int main() { int a, b; while (cin >> n >> m) { int ans = 0; fun(); while (n--) { scanf("%d%d", &a, &b); ans ^= sg[a][b]; } if (ans == 0) cout << "BAD LUCK!" << endl; else cout << "I WIN!" << endl; } }
hdu1730 hdu1848 hdu1536 hdu2873
http://acm.hdu.edu.cn/showproblem.php?pid=1907
以上是关于初学 博弈论 又称对策论 Game Theory的主要内容,如果未能解决你的问题,请参考以下文章
博弈论 斯坦福game theory stanford week 6.2_
博弈论 斯坦福game theory stanford week 3.2_
博弈论 斯坦福game theory stanford week 4.0_
博弈论 斯坦福game theory stanford week 1.1