SG函数:

给定一个有向无环图和一个起始顶点上的一枚棋子,两名选手交替的将这枚棋子沿有向边进行移动,无法移 动者判负。事实上,这个游戏可以认为是所有Impartial Combinatorial Games的抽象模型。也就是说,任何一个ICG都可以通过把每个局面看成一个顶点,对每个局面和它的子局面连一条有向边来抽象成这个“有向图游戏”。下 面我们就在有向无环图的顶点上定义Sprague-Garundy函数。首先定义mex(minimal excludant)运算,这是施加于一个集合的运算,表示最小的不属于这个集合的非负整数。例如mex{0,1,2,4}=3、mex{2,3,5}=0、mex{}=0。

在我的理解中,sg函数就是一个对有向无环图dfs的过程,在处理nim博弈时,多个石堆可以看成多个sg函数值的异或。

例题:

POJ2311 Cutting Game

典型的sg博弈,找后继状态。题意是给出一个n*m的纸片,每次剪成两部分,谁先剪到1*1就胜利。这就是一个找后继的题目,每次剪成的两部分就是前一状态的后继,只要将两个部分的sg值异或起来就是前一状态的sg值。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<set>
#include<vector>
#include<stack>
#define mem(a,b) memset(a,b,sizeof(a))
#define FOR(a,b,i) for(i=a;i<=b;++i)
#define For(a,b,i) for(i=a;i<b;++i)
#define N 1000000007
using namespace std;
inline void RD(int &ret)
{
char c;
do
{
c=getchar();
}
while(c<'0'||c>'9');
ret=c-'0';
while((c=getchar())>='0'&&c<='9')
{
ret=ret*10+(c-'0');
}
}
inline void OT(int a)
{
if(a>=10)
{
OT(a/10);
}
putchar(a%10+'0');
}
int sg[201][201];
int dfs(int a,int b)//sg函数的一般写法
{
if(sg[a][b]>=0)
{
return sg[a][b];
}
int i,map[201],r;//map标记数组一定要在dfs内部定义,不然会出现错误
mem(map,0);
FOR(2,(a/2),i)
{
r=dfs(i,b)^dfs(a-i,b);//后继的异或得到前一状态的sg值
map[r]=1;
}
FOR(2,(b/2),i)
{
r=dfs(a,i)^dfs(a,b-i);
map[r]=1;
}
FOR(0,200,i)
{
if(map[i]==0)
{
return sg[a][b]=i;//mex公式的应用
}
}
}
int main()
{
int n,m,sum;
mem(sg,-1);
while(scanf("%d%d",&n,&m)!=EOF)
{
sum=dfs(n,m);
if(sum>0)
{
printf("WIN\n");
}
else
{
printf("LOSE\n");
}
}
return 0;
}

POJ2425 A Chess Game

题意是给你一个拓扑图,一个起点上的n个棋子,两个玩家交替移动棋子,谁无法移动谁输,典型的sg函数运用。套用模板就行了。此题数据量较大,加入了输入优化后刷到了第一版第四名,nice!

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<set>
#include<vector>
#include<stack>
#define mem(a,b) memset(a,b,sizeof(a))
#define FOR(a,b,i) for(i=a;i<=b;++i)
#define For(a,b,i) for(i=a;i<b;++i)
#define N 1000000007
using namespace std;
inline void RD(int &ret)
{
char c;
do
{
c=getchar();
}
while(c<'0'||c>'9');
ret=c-'0';
while((c=getchar())>='0'&&c<='9')
{
ret=ret*10+(c-'0');
}
}
inline void OT(int a)
{
if(a>=10)
{
OT(a/10);
}
putchar(a%10+'0');
}
int vis[1001][1001],sg[1001];
int n;
int dfs(int x)//典型的sg过程
{
int i;
if(sg[x]!=-1)
{
return sg[x];
}
int f[1001];
mem(f,0);
For(0,n,i)
{
if(vis[x][i]!=-1)
{
f[dfs(i)]=1;
}
}
i=0;
while(f[i])
{
i++;
}
return sg[x]=i;
}
int main()
{
int i,j,k,t,x,p,sum;
while(scanf("%d",&n)!=EOF)
{
mem(vis,-1);
mem(sg,-1);
For(0,n,i)
{
RD(k);
if(k==0)
{
sg[i]=0;
}
For(0,k,j)
{
RD(t);
vis[i][t]=1;//建图
}
}
while(1)
{
RD(x);
if(x==0)
{
break;
}
sum=0;
For(0,x,i)
{
RD(p);
sum^=dfs(p);
}
if(sum!=0)
{
printf("WIN\n");
}
else
{
printf("LOSE\n");
}
}
}
return 0;
}

POJ2068 Nim

题意是圆桌上有2n个人,奇数一队,偶数一队,每个人都有一个拿走棋子的最高限额,问你最后1对能否获胜。

还是用强大的sg函数过的,记录下每个状态的sg。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<set>
#include<vector>
#include<stack>
#include <queue>
#include<map>
#define mem(a,b) memset(a,b,sizeof(a))
#define FOR(a,b,i) for(i=a;i<=b;++i)
#define For(a,b,i) for(i=a;i<b;++i)
using namespace std;
inline void RD(int &ret)
{
char c;
do
{
c=getchar();
}
while(c<'0'||c>'9');
ret=c-'0';
while((c=getchar())>='0'&&c<='9')
{
ret=ret*10+(c-'0');
}
}
inline void OT(int a)
{
if(a>=10)
{
OT(a/10);
}
putchar(a%10+'0');
}
int n,s,m[22],sg[22][8200],sum;
int dfs(int x,int y)
{
if(sg[x][y]!=-1)
{
return sg[x][y];
}
int i,j;
FOR(1,m[x],i)
{
if(y-i<0)
{
break;
}
if(x+1>=2*n)
{
j=0;
}
else
{
j=x+1;
}
if(dfs(j,y-i)==0)
{
return sg[x][y]=1;
}
}
return sg[x][y]=0;
}
int main()
{
int i;
while(1)
{
RD(n);
if(n==0)
{
break;
}
RD(s);
For(0,2*n,i)
{
RD(m[i]);
}
mem(sg,-1);
FOR(0,2*n,i)
{
sg[i][0]=1;
}
sum=dfs(0,s);
if(sum==0)
{
printf("0\n");
}
else
{
printf("1\n");
}
}
return 0;
}

POJ3537 Crosses and Crosses

题意:给出一个1*n的矩形,上面有n个方格,现有两人分别在上面画×,谁先能画出三个×相连就赢了。这就是一个sg函数的母问题转化为子问题的题目,由于在第x位置画了×后,则就转变成(x-3)个格子画×和(n-x-2)个格子画×。。。这就能不断的分解下去,最后将所有sg值异或起来就是正解了。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<set>
#include<vector>
#include<stack>
#include <queue>
#include<map>
#define mem(a,b) memset(a,b,sizeof(a))
#define FOR(a,b,i) for(i=a;i<=b;++i)
#define For(a,b,i) for(i=a;i<b;++i)
using namespace std;
inline void RD(int &ret)
{
char c;
do
{
c=getchar();
}
while(c<'0'||c>'9');
ret=c-'0';
while((c=getchar())>='0'&&c<='9')
{
ret=ret*10+(c-'0');
}
}
inline void OT(int a)
{
if(a>=10)
{
OT(a/10);
}
putchar(a%10+'0');
}
int sg[2001];
int dfs(int x)
{
if(x<0)
{
return 0;
}
if(sg[x]>=0)
{
return sg[x];
}
int i,y;
bool v[2001]={false};
FOR(1,x,i)
{
y=dfs(i-3)^dfs(x-i-2);//找后继(经典)
v[y]=true;
}
for(i=0;;i++)
{
if(v[i]==false)
{
return sg[x]=i;
}
}
}
int main()
{
int n,sum;
mem(sg,-1);
while(scanf("%d",&n)!=EOF)
{
sum=dfs(n);
if(sum)
{
printf("1\n");
}
else
{
printf("2\n");
}
}
return 0;
}

POJ2599 A funny game

记忆化搜索,这题的博弈味道不浓,更多的是搜索。题意是给一个图,两人轮流移动,走过的节点不能再走。水题,dfs+标记就行。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<set>
#include<vector>
#include<stack>
#define mem(a,b) memset(a,b,sizeof(a))
#define FOR(a,b,i) for(i=a;i<=b;++i)
#define For(a,b,i) for(i=a;i<b;++i)
#define N 1000000007
using namespace std;
inline void RD(int &ret)
{
char c;
do
{
c=getchar();
}
while(c<'0'||c>'9');
ret=c-'0';
while((c=getchar())>='0'&&c<='9')
{
ret=ret*10+(c-'0');
}
}
inline void OT(int a)
{
if(a>=10)
{
OT(a/10);
}
putchar(a%10+'0');
}
int n,v[1001][1001],vis[1001];
int dfs(int x)
{
int i;
FOR(1,n,i)
{
vis[x]=1;
if(v[i][x]&&!vis[i])
{
if(!dfs(i))
{
vis[x]=0;
return i;
}
}
vis[x]=0;
}
return 0;
}
int main()
{
int m,i,a,b;
while(scanf("%d%d",&n,&m)!=EOF)
{
mem(v,0);
mem(vis,0);
FOR(1,n-1,i)
{
RD(a);
RD(b);
v[a][b]=v[b][a]=1;
}
i=dfs(m);
if(i!=0)
{
printf("First player wins flying to airport %d\n",i);
}
else
{
printf("First player loses\n");
}
}
return 0;
}

博弈问题之SG函数博弈小结的更多相关文章

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

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

  2. 【转】博弈问题及SG函数(真的很经典)

    博弈问题若你想仔细学习博弈论,我强烈推荐加利福尼亚大学的Thomas S. Ferguson教授精心撰写并免费提供的这份教材,它使我受益太多.(如果你的英文水平不足以阅读它,我只能说,恐怕你还没到需要 ...

  3. (转)博弈问题与SG函数

    博弈问题若你想仔细学习博弈论,我强烈推荐加利福尼亚大学的Thomas S. Ferguson教授精心撰写并免费提供的这份教材,它使我受益太多.(如果你的英文水平不足以阅读它,我只能说,恐怕你还没到需要 ...

  4. 转载--博弈问题及SG函数(真的很经典)

    博弈问题若你想仔细学习博弈论,我强烈推荐加利福尼亚大学的Thomas S. Ferguson教授精心撰写并免费提供的这份教材,它使我受益太多.(如果你的英文水平不足以阅读它,我只能说,恐怕你还没到需要 ...

  5. SG函数博弈——poj2311

    关于SG函数的博弈 首先定义必败态 x : SG[x]=0 设任意一个状态y,到所有y能到达的状态连一条边,令这些后继为z y : SG[y]=mex(SG[z]) SG[y]==0 : y就是必败态 ...

  6. hdu 1847 博弈基础题 SG函数 或者规律2种方法

    Good Luck in CET-4 Everybody! Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K ...

  7. HDU1536&&POJ2960 S-Nim(SG函数博弈)

    S-Nim Time Limit: 2000MS   Memory Limit: 65536KB   64bit IO Format: %I64d & %I64u Submit Status ...

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

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

  9. HDU 5724 Chess (状态压缩sg函数博弈) 2016杭电多校联合第一场

    题目:传送门. 题意:有n行,每行最多20个棋子,对于一个棋子来说,如果他右面没有棋子,可以移动到他右面:如果有棋子,就跳过这些棋子移动到后面的空格,不能移动的人输. 题解:状态压缩博弈,对于一行2^ ...

随机推荐

  1. STL 源代码剖析 算法 stl_algo.h -- partition

    本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie partition ------------------------------------ ...

  2. L型代码结构案例:Link访问权限(上)

    这是松结对编程的第20篇(专栏目录). 本文探讨Link访问权限的最佳实现方法,力求外观干净且封装良好. 这些代码将位于L型代码结构(参见松结对编程系列中的定义)的下层,调用者无需理解其原理. 顺便说 ...

  3. BZOJ 1053 反素数ant

           初读这道题,一定有许多疑惑,其中最大的疑惑便是"反素数",反素数的概念很简单,就是,a<b同时a的因数个数大于b的因数个数.但是想要完成本题还需要一些信息,关于 ...

  4. dhtmlx使用学习

    Var tabbar=new dhtmlXTabBar("tab","top"); tabbar.setImagePath("./tabbar/cod ...

  5. Android 使用 array.xml

    //获取文件资源 TypedArray mainNavIcon = context.getResources().obtainTypedArray(R.array.mainNavIcon); //获取 ...

  6. vim 小技巧总结

    1.v+移动光标可以选中文本. 2.y可以复制已经选中的文本 3.p可以粘贴 复制一行则:yy 复制当前光标所在的位置到行尾:y$ 复制当前光标所在的位置到行首:y^ 复制三行则:3yy,即从当前光标 ...

  7. 笔记之Cyclone IV第一卷第三章器件中的存储器模块

    嵌入式存储器结构由一列列 M9K 存储器模块组成,通过对这些 M9K 存储器模块进行配置,可以实现各种存储器功能,例如:RAM.移位寄存器. ROM 以及FIFO 缓冲器. M9K 存储器模块支持以下 ...

  8. 引用 RAM和ROM和Flash ROM的区别

    用 饿狼 的 RAM和ROM和Flash ROM的区别 RAM(Random Access Memory)的全名为随机存取记忆体,它相当于PC机上的移动存储,用来存储和保存数据的.它在任何时候都可以读 ...

  9. C++的Json解析库:jsoncpp和boost

    C++的Json解析库:jsoncpp和boost - hzyong_c的专栏 - 博客频道 - CSDN.NET C++的Json解析库:jsoncpp和boost 分类: 网络编程 开源库 201 ...

  10. python模块学习---HTMLParser(解析HTML文档元素)

    HTMLParser是Python自带的模块,使用简单,能够很容易的实现HTML文件的分析. 本文主要简单讲一下HTMLParser的用法. 使用时需要定义一个从类HTMLParser继承的类,重定义 ...