巴什博奕:

两个顶尖聪明的人在玩游戏,有n个石子,每人可以随便拿1−m个石子,不能拿的人为败者,问谁会胜利

结论:

设当前的石子数为\(n=k∗(m+1)\)即\(n%(m+1)==0\)时先手一定失败

HDU1846

#include<iostream>
using namespace std;
int main() {
int C,N,M;
scanf("%d",&C);
while(C--) {
scanf("%d%d",&N,&M);
if(N%(M+1)==0) printf("second\n");
else printf("first\n");
}
return 0;
}

HDU4764

#include<cstdio>
#include<algorithm>
const int MAXN=1e6+10,INF=1e9+10;
int main() {
int x,y;
while(scanf("%d%d",&x,&y)) {
if(!x&&!y) break;
if( (x-1)%(y+1)==0 ) puts("Jiang\n");
else puts("Tang\n");
}
return 0;
}

nim游戏:

有两个顶尖聪明的人在玩游戏

有n堆石子,两个人可以从任意一堆石子中拿任意多个石子(不能不拿),没法拿的人失败。问谁会胜利

结论:

当\(n\)堆石子的数量异或和等于\(0\)时,先手必败,否则先手必胜

简证:

定义当前状态为

P-position:在当前的局面下,先手必败

N-position:在当前的局面下,先手必胜

对于面对局面为

\(0\ xor\ 0\ xor\ ...\ 0 = 0\)称它为p局面

若当前局面为

\(a_1\ xor\ a_{2}\ ...a_n = k\) 来说

我们可以通过改变一个\(a_i\)的比如\(a_i\ xor \ k\)值来使得上式的值为0,此时修改该操作的人为必败局面,由于\(xor\)计算的特殊性,我们知道一定有一个\(a_i\)最高位与\(k\)最高位的1是相同的,那么必然有\(a_i\ xor\ k\ <a_i\)的,所以操作可为减法

对于局面

\(a_1\ xor\ a_{2}\ ...a_n = 0\)

那么此时对于任何一种操作都会使得上式的值发生改变,也就是不为0

对与xor的性质,当每个位置的1都存在偶数个时,异或和为0,只改变一个\(a_i\),一定会使得某个位置的1的个数发生改变

所以只有当先手面临\(xor\)和为0是,必胜

luogu P2197 nim游戏

#include<cstdio>
#include<iostream>
#include<algorithm>
int main() {
int T;
//scanf("%d",&T);
std::cin>>T;
for(int n;T--;) {
std::cin>>n;
int tmp=0;
for(int q,i=1;i<=n;++i) {
std::cin>>q;tmp^=q;
}
if(!tmp)puts("No");
else puts("Yes");
}
//char a[10];scanf("%s",a);puts(a);
return 0;
}

SG函数

SG函数内容的理解借鉴自zyf学长,鸣谢!QUQ

对于nim游戏,你已经知道怎么做了

但是,要是稍加改变游戏规则,那么恐怕无法很快找出必胜策略

我们可以吧游戏抽象为一张图,每一个点代表一个状态,对于每个状态与他的子状态连一条有向边。那么,我们在这张有向无环图上引入SG函数

定义

定义运算\(mex()\),这是对于一个集合的运算,表示最小的不属于该集合的非负整数

例如\(mex\{0,1,2,3\}=4;mex\{2,3,5\}=0\)。

那么对于每个顶点的SG值为:

后继中未出现过的最小值

\[sg(x)=mex\{sg(y)\}
\]

y为x的后继

SG函数的性质

首先对于出度为0的点,他的SG值为0

对于一个SG值为0的点x,他的后继y都满足sg(y)!=0,y的后继中一定有存在一个点的SG值为0

诶,是不是和刚才nim游戏很像

所以当SG(x)=0时,x代表了p局面

扩展

考虑有向图上有n个棋子,每次可以移动一颗,不能移动者lose,这时候怎样找到必胜策略呢?

我们来考虑每个顶点的SG值得含义,当\(sg(x)=k\)时,x的后继y的\(sg(y)=1\ ->\ k\)也就是说,某个棋子进行移动后sg值是一定会发生改变的

类比普通的nim游戏,Nim游戏的一种必胜策略就是把\(a_i\)变为k,

那么,把第i个棋子移动到一个SG值为k的点上就是一种必胜策略

hhhh,也就是说Nim游戏的一种必胜策略对应着这n个棋子的必胜策略

  • 那么这个游戏P局面就棋子所在位置的SG函数异或和为0的时候。

经典的博弈问题

再来说一下几类经典的博弈问题

Anti-Nim游戏和SJ定理

有两个顶尖聪明的人在玩游戏(他们一个是崩崩崩玩家一个是舰娘玩家),游戏规则是这样的:

有n堆石子,两个人可以从任意一堆石子中拿任意多个石子(不能不拿),拿走最后一个石子的人失败。问谁会胜利?

结论

先手必胜当且仅当:

所有堆的石子数都为1且游戏的SG值为0

有些堆的石子数大于1且游戏的SG值不为0

证明

1)游戏有三种情况

每堆只有一个石子

  • 异或和为0先手必胜
  • 异或和不为0先手必败

    2)只存在一堆石子数>1,则先手必胜

    可以发现,在这种情况下异或值一定 != 0

    并且先手一定可以使剩下的奇数个石子变为个数为1的堆

    3)存在2堆以上的石子数>1

    -异或和=0时先手必败

    -异或和!=0先手必胜

    那么

    考虑nim游戏,当异或和=0时,洗衣不操作都会使异或和!=0,相反同样....符合NP状态的转换

    不断进行转换会在一个时刻变为 ‘只有一堆大于1' 的局面,而该局面一定是有 ‘两堆>1且异或和=0’ 的局面 转来的,所以 ‘两堆>1且异或和=0’ 的局面一定会在某一时刻转化为 ‘只有一堆大于1’ 的 先手必胜局面 ,也就是说是先手必败态;

    其他同理可证

    bzoj 1022: [SHOI2008]小约翰的游戏John
#include<cstdio>
#include<algorithm>
inline int read() {
int x=0,f=1;
char c=getchar() ;
while(c<'0'||c>'9') {
if(c=='-')f=-1;
c=getchar();
}
while(c<='9'&&c>='0')x=x*10+c-'0',c=getchar();
return x*f;
}
int a;
int main() { for(int n,T=read();T--;) {
n=read();bool op=1;int tmp=0;
for(int i=1;i<=n;++i) {
a=read();if(a!=1)op=0;
tmp^=a;
}
if((op&&!tmp) || (!op&&tmp))puts("John");
else puts("Brother");
}
return 0;
}

SJ定理

定理:SJ定理

对于任意一个Anti-SG游戏,如果我们规定当局面中所有的单一游戏的SG值为0时,游戏结束,则先手必胜当且仅当:

(1)游戏的SG函数不为0且游戏中某个单一游戏的SG函数大于 1; (2)游戏的SG函数为0且游戏中没有单一游戏的SG函数大于1。

Multi-SG

有n堆石子,两个人可以从任意一堆石子中拿任意多个石子(不能不拿)或把一堆数量不少于2石子分为两堆不为空的石子,没法拿的人失败。问谁会胜利

分析

用sg函数来解决

操作1与nim游戏无异,对与一个石子数为k的点来说,后继可以为1..k

操作二实际上是将一个游戏分解为两个游戏,根据SG定理,两个有的的和为两个游戏的SG函数值得异或,我们可以通过异或运算把两个单一游戏连接到一起,作为一个后继状态

要是数据范围很大,SG函数就不能用了,有结论

\[SG\left( x\right) =\begin{cases}x-1\left( x\mod4=0\right) \\ x\left( x\mod4=1 \lor 2\right) \\ x+1\left( x\mod4=3\right) \end{cases}
\]

hhhhhhhhhhh

定义

根据上面的游戏,定义Multi-SG游戏

Multi-SG 游戏规定,在符合拓扑原则的前提下,一个单一游戏的后继可以为多个单一游戏。

  • Multi-SG其他规则与SG游戏相同。注意在这里要分清楚后继与多个单一游戏

例题

HDU 3032

#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=1001;
int read() {
char c=getchar();int x=0,f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
int a[maxn],sg[maxn];
int main() {
int T=read();
while(T--) {
int n=read();
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<=n;i++)
if(a[i] % 4 == 0) sg[i] = a[i]-1;
else if(a[i]%4==1||a[i]%4==2) sg[i] = a[i];
else sg[i] = a[i]+1;
int ans=0;
for(int i=1;i<=n;i++)
ans^=sg[i];
if(ans)puts("Alice");
else puts("Bob");
}
return 0;
}

POJ 2311

#include<cstdio>
#include<cstring>
const int maxn=3824;
int read() {
char c=getchar();int x=0,f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
int sg[maxn][maxn];//当前剩余i行 j列的sg函数
int vis[maxn];
int main() {
int n=201,m=201;
for(int i=2;i<=n;i++)
for(int j=2;j<=n;j++) {
memset(vis,0,sizeof(vis));
for(int k=2;k<=i-2;k++) vis[sg[k][j]^sg[i-k][j]]=1;
for(int k=2;k<=j-2;k++) vis[sg[i][k]^sg[i][j-k]]=1;
for(int k=0;;k++) if(!vis[k]) {sg[i][j]=k;break;}
}
while(scanf("%d%d",&n,&m)!=EOF)
puts(sg[n][m] ? "WIN" : "LOSE");
return 0;
}

BZOJ 2940

BZOJ 1188

洛谷 3235

Every-SG

给定一张无向图,上面有一些棋子,两个顶尖聪明的人在做游戏,每人每次必须将可以移动的棋子进行移动,不能移动的人输

分析

这样的话,能赢得游戏必须赢

为了赢得最后的胜利

当你知道某一个单一游戏一定会输的话,你需要尽力缩短游戏的时间,相反的当你知道某一个游戏一定会赢的话,那么就需要尽力延长游戏的时间

定义:

  • 对于还没有结束的单一游戏,游戏者必须对该 游戏进行一步决策;
  • Every-SG游戏的其他规则与普通 SG游戏相同

Every-SG游戏与普通游戏最大的不同就是他多了一维时间

对于SG的值为0,我们需要周到最少走多少步才能结束,对于SG值不为0的点我们需要最多走多少步结束

我们用step变量来记录这个步数

\[step(u) = \begin{cases} 0, & \text{$u为终止状态$}\\ max\{step(v)\}, & \text{ $sg(u)\neq 0\land v为u的后继\land sg(v)=0$ }\\ min\{step(v)\}, & \text{$sg(u)=0\land v为u的后继$} \end{cases}
\]

定理

对于Every-SG游戏先手必胜当且仅当单一游戏中最大的step为奇数。

定理是显然的:

对于Every-SG游戏先手必胜当且仅当单一游戏中最大的step为奇数。

定理是显然的:最大的单一游戏步数如果是奇数的话那么肯定是先手取得进行最后一步,否则一定是对手取走最后一个棋子。

HDU 3595 GG and MM

balabala//我还没写QUQ

博弈论与SG函数的更多相关文章

  1. 博弈论(SG函数):HNOI 2007 分裂游戏

    Description 聪聪和睿睿最近迷上了一款叫做分裂的游戏. 该游戏的规则试: 共有 n 个瓶子, 标号为 0,1,2.....n-1, 第 i 个瓶子中装有 p[i]颗巧克力豆,两个人轮流取豆子 ...

  2. CF 256C Furlo and Rublo and Game【博弈论,SG函数】

    暴力的求SG函数会超时,正解是先处理出10^6以内的SG值,对于更大的,开根号之后计算出. 小数据观察可以发现sg函数值成段出现,而且增长速度很快,因此可以计算出来每一段的范围,只需打表即可. Nim ...

  3. 0x3A 博弈论之SG函数

    博弈即玄学啊 (除了nim和二分图博弈什么都不会 算是学了下SG函数吧 这个东西是针对有向图游戏的,相当于把一个局面看作一个点,到达下个局面相当于建一条边 必胜态SG值为0 那么对于一个点,他的SG值 ...

  4. ABC206 F - Interval Game 2 (区间DP,博弈论,SG函数)

    题面 题意很简单 A l i c e \tt Alice Alice 和 B o b \tt Bob Bob 在博弈.摆在他们面前有 N \rm N N 个区间 [ l i , r i ) \rm[l ...

  5. 博弈论之SG函数

    Fibonacci again and again(http://acm.hdu.edu.cn/showproblem.php?pid=1848) Time Limit: 1000/1000 MS ( ...

  6. 博弈论进阶之SG函数

    SG函数 个人理解:SG函数是人们在研究博弈论的道路上迈出的重要一步,它把许多杂乱无章的博弈游戏通过某种规则结合在了一起,使得一类普遍的博弈问题得到了解决. 从SG函数开始,我们不再是单纯的同过找规律 ...

  7. [您有新的未分配科技点]博弈论入门:被博弈论支配的恐惧(Nim游戏,SG函数)

    今天初步学习了一下博弈论……感觉真的是好精妙啊……希望这篇博客可以帮助到和我一样刚学习博弈论的同学们. 博弈论,又被称为对策论,被用于考虑游戏中个体的预测行为和实际行为,并研究他们的应用策略.(其实这 ...

  8. Nim游戏与SG函数 ——博弈论小结

    写这篇博客之前,花了许久时间来搞这个SG函数,倒是各路大神的论文看的多,却到底没几个看懂的.还好网上一些大牛博客还是性价比相当高的,多少理解了些,也自己通过做一些题加深了下了解. 既然是博弈,经典的N ...

  9. 博弈论基础之sg函数与nim

    在算法竞赛中,博弈论题目往往是以icg.通俗的说就是两人交替操作,每步都各自合法,合法性与选手无关,只与游戏有关.往往我们需要求解在某一个游戏或几个游戏中的某个状态下,先手或后手谁会胜利的问题.就比如 ...

随机推荐

  1. oracle大数据匹配处理C#

    忙碌了几天写出来的oracle存储过程在作业中执行. 写的oracle存储过程如果有什么不好的地方大家指点指点. oracle存储过程其中使用到游标嵌套.if.if嵌套.数据插入表.select插入表 ...

  2. taotao用户注册前台页面

    注册页面效果: 注册jsp: <%@ page language="java" contentType="text/html; charset=UTF-8" ...

  3. [Usaco2015 dec]Max Flow 树上差分

    [Usaco2015 dec]Max Flow Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 353  Solved: 236[Submit][Sta ...

  4. Linux内存 性能调优

    内存是影响Linux性能的主要因素之一,内存资源的充足与否直接影响应用系统的使用性能. free命令:监控Linux内存使用状况. 由上图可知,空闲内存是free+buffers+cached=155 ...

  5. Every Programmer Should Know These Latency Numbers

    Every Programmer Should Know These Latency Numbers 1秒=1000毫秒(ms) 1秒=1,000,000 微秒(μs) 1秒=1,000,000,00 ...

  6. 1、linux下mysql5.5.20安装过程报错汇总

    1.Access denied for user 'root'@'localhost' (using password: YES) 这个提示是因为root帐户默认不开放远程访问权限,所以需要修改一下相 ...

  7. 精通javascript笔记(智能社)——简易tab选项卡及应用面向对象方法实现

    javascript代码(常规方式/面向过程): <script type="text/javascript"> window.onload=function(){ v ...

  8. bzoj2811 [Apio2012]Guard

    传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2811 [题解] 首先我们先把没看到忍者的段去掉,可以用线段树做. 如果剩下的就是K,那么特判 ...

  9. 【洛谷 SP283】NAPTIME - Naptime(DP)

    题目链接 先考虑如果只有一天,那么该怎么做. 设\(f[i][j][1]\)表示前\(i\)个小时睡了\(j\)个小时并且第\(j\)个小时正在睡觉时的最大体力,\(f[i][j][1]\)表示前\( ...

  10. asp单页面301跳转

    <% Response.Status="301 Moved Permanently"Response.AddHeader "Location", &quo ...