这个标题是不是看起来很厉害呢...

我们首先来看一个最简单的游戏。比如我现在有一堆石子,有p个,每次可以取走若干个(不能不取),不能取的人就输了。

现在假设有两个人要玩这个游戏,一个人先手,一个人后手,假设两个人都是足够聪明的AI,那么谁会赢?

显然p≠0时先手赢,他只要全部取完就行了...

我们先不管这个游戏有多傻逼,我们看一看这个游戏所隐含的模型。

比如我们把当前游戏局面抽象成一个点,把这个点往每下一步可以到达的新状态连一个边,这样就形成了一个有向无环图。(如果有环这个游戏就不会结束了)

现在我们来定义sg函数。sg(T),T表示一个状态,当T=0先手必败,其他情况后手必败。

你说这玩意儿是啥?定义怎么这么不清楚

没关系我们先来看一个新函数bool_sg(T),当T=0时先手必败,T=1时后手必败。

那么我们发现bool_sg(结束态)=0,其他情况下只有当一个状态的后继状态后手都必败时才为先手必败(显然吧)。

sg函数与它类似,不过相对更为复杂。sg(结束态)=0。

其他情况下sg(T)=mex{sg(G)} (G为T的后继状态,mex表示不在集合中的最小非负整数)

可以发现bool_sg(T)=(bool)sg(T) (也很显然吧)。

我们上面说的这个游戏中,假设我们用p这个数来表示p个石子的状态。那么可以发现sg(p)=p (p>=0)。

所以我们也可以发现先手在p>0时必胜(mdzz)。

虽然上面这个游戏的解只要目测就可以得出,但在比较复杂一点的游戏中它就能派上用场了。

比如每次要取一个斐波那契数?我们只要求sg的时候开个数组模拟mex的过程就可以得到一个平方做法啦!

例1 nim游戏

现在有n堆石子,每一次你可以从任意一堆中任意个石子,但不能不选。如果你不能选你就输了。

你是先手,现在你想知道你有没有必胜策略。

如果是一堆石子,这个sg函数就是之前说过的sg(p)=p。

现在有多堆石子了,该怎么办呢?

我们定义一些游戏的和为你可以轮流从几个游戏中任意选择一个进行操作,那么有一个性质:

一些游戏的和中状态的sg函数等于这些子游戏状态sg函数的异或值。

至于为什么是这样?其实我也不知道2333 这当然是可以证明的,想要证明可以百度啊

例2 hdu1848

一共三堆石子,分别m、n、p个。两人轮流取石子,每步可以从任意一堆中取出一个斐波那契数列中的元素,但不能不取,不能取的人就输了。假设双方都使用最优策略,请判断先手的人会赢还是后手的人会赢。

多组数据,1<=m,n,p<=1000

如果只有一堆石子,可以用一个bool数组暴力求出sg函数。那么有三堆石子只要异或在一起就行了。

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
#define SZ 666666
int fib[SZ+5],sg[SZ+5];
bool ok[SZ+5];
#define P 2333
int main()
{
fib[0]=fib[1]=1;
int S=20;
for(int i=2;i<=S;i++) fib[i]=fib[i-1]+fib[i-2];
sg[0]=0;
for(int i=1;i<=2000;i++)
{
for(int j=0;j<P;j++) ok[j]=0;
for(int j=0;j<=S;j++)
{
if(fib[j]<=i) ok[sg[i-fib[j]]]=1;
}
for(int j=0;;j++)
{
if(!ok[j]) {sg[i]=j; break;}
}
}
int n,m,p;
while(scanf("%d%d%d",&n,&m,&p),n||m||p)
{
int sgg=sg[n]^sg[m]^sg[p];
if(sgg) puts("Fibo"); else puts("Nacci");
}
}

例3 hdu1850

现在还是有n堆石子,第i堆有i个石子,不能不选。如果你不能选你就输了。

你是先手,现在你只想知道你为了必胜,第一步有几种走法。

首先这还是一个nim游戏的模型,不过改成了求方案数。

如果不能必胜可以简单地输出0,否则我们求出整个游戏起始态的sg函数,假设叫s好了。

那我们可以知道s=1^2^3^4^....^n对吧。

那我们考虑第一次假设选第i堆,我们发现我们目标是这次选完让先手必败(因为这里的先手就是后手)即选完让sg函数为0。

那我们可以发现不考虑这一堆剩下的sg函数就是(s^i),那为了让选完sg函数为0,那么这堆剩下的就要是(s^i)。

于是我们发现只要判一下(s^i)<=i是否成立就行了,如果成立我们就可以一开始在i堆中选(i-(s^i))个。这也是一般博弈问题中求第一步走法的一般思路。

sg函数的基本东西讲完了,接下来是一些(相对)比较复杂(鬼畜)的应用。

例4 hdu1517

有一个数p,开始p=1,两人轮流操作,每一次可以使p乘上2~9的一个整数,最先使得p>=n的人赢。

给出n,求最优策略下先手是必胜还是必败。

1<n<4294967295

可以发现当<4294967295的只有2、3、5、7这些质因子的数直观上感觉不是很多,暴力跑一下发现只有6k+个。

然后我们可以用2、3、5、7的质因数个数来表示一个数,然后暴力sg一波~

需要注意的是这里是p>=n的人赢...不过没关系就当做是输吧,输出的时候再反过来。

//By zzq
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
#include <string.h>
#include <math.h>
#include <set>
#include <map>
using namespace std;
typedef long long ll;
ll n;
map<ll,int> sgs;
int sg(ll cur)
{
if(cur>=n) return 0;
int& mp=sgs[cur];
if(mp) return mp-1;
bool ff[6700];
memset(ff,0,sizeof(ff));
for(int i=2;i<=9;i++) ff[sg(cur*i)]=1;
for(int p=0;;p++)
{
if(!ff[p]) {mp=p+1; return p;}
}
}
int main()
{
while(cin>>n)
{
sgs.clear();
int ans=sg(1);
if(ans) puts("Stan wins."); else puts("Ollie wins.");
}
}

例5 bzoj1188

还是n堆石子,这回要求每次选出3个数i,j,k,1<=i<j<=k<=n,i堆中至少要有一个石子,那么在i堆中取出一个石子,在j、k堆中各放入一个,当j=k时就在j堆中放入两个,无法选出的人就输了。

求是否有必胜策略及必胜策略下第一步字典序最小的走法(i,j,k)。

1<n<=21,1<=每堆石子数<=10000。

我们可以发现一些性质,题目只和每堆石子的奇偶性有关,所以我们可以把每堆石子数视为mod 2意义下的。

然后我们还可以发现每堆石子是完全独立的,就是说我们可以用每一堆的异或在一起得到总的sg值。

发现这两个性质以后就比较可做了。

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
#define SZ 666666
int T,n,a[SZ],sg[SZ];
bool gg[SZ];
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
int shit=1;
while(shit<=n) shit*=2;
shit*=2;
for(int i=1;i<=n;i++) scanf("%d",a+i), a[i]&=1;
sg[n]=0;
for(int i=n-1;i>=1;i--)
{
for(int j=0;j<shit;j++) gg[j]=0;
for(int j=i+1;j<=n;j++)
{
for(int k=j;k<=n;k++) gg[sg[j]^sg[k]]=1;
}
for(int j=0;;j++)
{
if(!gg[j]) {sg[i]=j; break;}
}
}
int ans=0,gs=0;
for(int i=1;i<=n;i++) if(a[i]) ans^=sg[i];
for(int i=1;i<=n;i++)
{
for(int j=i+1;j<=n;j++)
{
for(int k=j;k<=n;k++)
{
if(ans^sg[i]^sg[j]^sg[k]) continue;
++gs; if(gs==1) printf("%d %d %d\n",i-1,j-1,k-1);
}
}
}
if(!gs) puts("-1 -1 -1");
printf("%d\n",gs);
}
}

这方面题做得也不多,今天就写到这里吧。

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

  1. sg函数与博弈论2

    参考链接: http://blog.sina.com.cn/s/blog_51cea4040100h3l9.html 这篇主要就是讲anti-sg.multi-sg和every-sg的. 例1 poj ...

  2. 博弈论进阶之SG函数

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

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

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

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

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

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

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

  6. 【基础操作】博弈论 / SG 函数详解

    博弈死我了……(话说哪个小学生会玩博弈论提到的这类弱智游戏,还取石子) 先推荐两个文章链接:浅谈算法——博弈论(从零开始的博弈论) 博弈论相关知识及其应用 This article was updat ...

  7. 博弈论 | 详解搞定组合博弈问题的SG函数

    本文始发于个人公众号:TechFlow,原创不易,求个关注 今天这篇是算法与数据结构专题的第27篇文章,我们继续深入博弈论问题.今天我们要介绍博弈论当中非常重要的一个定理和函数,通过它我们可以解决许多 ...

  8. 博弈论(nim游戏,SG函数)

    说到自己,就是个笑话.思考问题从不清晰,sg函数的问题证明方法就在眼前可却要弃掉.不过自己理解的也并不透彻,做题也不太行.耳边时不时会想起alf的:"行不行!" 基本的小概念 这里 ...

  9. POJ2425 A Chess Game[博弈论 SG函数]

    A Chess Game Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 3917   Accepted: 1596 Desc ...

随机推荐

  1. Net.Sf.Json java Object to JsonObject

    public class People{ private String name; public void setName(String name){ this.name = name; } publ ...

  2. iOS 设备标识

    //UUID    NSLog(@"-=-=-=-=-=-=-=-==-=-==qqqqqqqqqqqqqqqqqqqqqqq:::::::::::%@", [[NSUUID UU ...

  3. View的事件体系

    View的滑动 实现手段 优点 缺点 备注 scrollTo/scrollBy 使用简单 只能滑动view的内容,并不会滑动view本身. 且内容超出view本身的布局范围部分的不会显示 不适合有交互 ...

  4. 如何为Swift进行宏定义

    这阵子一直在自学Swift, 因为之前iOS的开发一直用Objective-C, 所以习惯了C语言那种宏定义方式, Swift作为一款更加安全的语言, 放弃了C语言中的宏定义, 有效的防止预编译时代码 ...

  5. 【代码笔记】iOS-改变文字输入框背景

    一,效果图. 二,工程图. 三,代码. RootViewController.h #import <UIKit/UIKit.h> @interface RootViewController ...

  6. Android中应用程序清除data/data,清除cache,超详细

    清除data,清除cache,其实在Android原生Setting里面有这个功能的. 需求是把这个功能做到自己的App里面,并计算出cache和data的size. 所以参考了一下Setting的源 ...

  7. [转]android笔记--Intent和IntentFilter详解

    Intent用于启动Activity, Service, 以及BroadcastReceiver三种组件, 同时还是组件之间通信的重要媒介. 使用Intent启动组件的优势1, Intent为组件的启 ...

  8. android Gui系统之SurfaceFlinger(3)---SurfaceFlinger

    7.SurfaceFlinger SurfaceFlinger在前面的篇幅了,多有涉及. SurfaceFlinger是GUI刷新UI的核心,所以任何关于SurfaceFlinger的改进都会对and ...

  9. jsp学习笔记一

    page属性 定义JSP文件中的全局属性. 实例: <%@ page language="java" contentType="text/html; charset ...

  10. Linux 折腾汇集,实时更新

    一.Linux教程 入门教程:http://www.92csz.com/study/linux/ 命令大全:http://man.linuxde.net/ 一.界面: 在Ubuntu.Linux Mi ...