SG函数

为了更一般化博弈问题,我们引入SG函数

SG函数有如下性质:

1.如果某个状态SG函数值为0,则它后继的每个状态SG函数值都不为0

2.如果某个状态SG函数值不为0,则它至少存在一个后继的状态SG函数值为0

如果某个局面SG函数值为0,则该局面先手必败

放到有向图中,该有向图的核就是SG值为0的点构成的集合

游戏的和

游戏的和的SG函数值=所有子游戏SG函数值的异或和Xor

如果所有子游戏都进行完毕,那么Xor=0,必败

如果某个状态的SG函数值为0,那么后手一定可以做出一种动作,保持Xor=0,那么先手必败。

反之某个状态的SG函数值不为0,先手可以让Xor=0,变成后手,重复上述动作,那么先手必胜

这样就能轻松合并多个独立的组合游戏啦

mex函数

$sg[$当前局面$]=mex(sg[$后继局面$])$

$mex$函数表示第一次还没出现的数

某种后继局面可能是很多个子游戏,那么该后继局面的$sg$函数就是这些子游戏的和,即子游戏$sg$函数的异或和

SG组合游戏

NIM游戏

有n堆石子,两个人玩游戏,每次轮流在一堆里取走任意个,取走最后一堆的最后一个石子的人赢,问谁赢

一堆石子相当于一个子游戏,显然该子游戏的SG函数值为该堆中石子数

再根据游戏的和的思想,把子游戏合并就能求出谁赢了

NIMk游戏

同样是NIM游戏,现在变成了每次在k堆中取任意个

NIM游戏采取策略的根本是,保证当SG函数值为0时,不论先手如何操作,后手一定能做出一种动作,保持Xor=0

把每堆石子都转化成k+1进制数,再进行不进位的加法即可

SG组合游戏

POJ 2960 S-Nim (SG函数递推)

裸题,考察对SG函数的理解。利用游戏的和以及mex函数。暴力递推出石子SG函数即可

 #include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N1 10010
#define ll long long
#define ull unsigned long long
using namespace std; const int inf=0x3f3f3f3f;
int gint()
{
int ret=,fh=;char c=getchar();
while(c<''||c>''){if(c=='-')fh=-;c=getchar();}
while(c>=''&&c<=''){ret=ret*+c-'';c=getchar();}
return ret*fh;
} int n,m,o,T,A,B,de;
int s[N1],a[N1],sg[N1],use[N1]; int main()
{
while(scanf("%d",&m)) { if(m==) break;
int i,j,ans=,flag;
memset(sg,,sizeof(sg));
for(i=;i<=m;i++) s[i]=gint();
sg[]=;
for(i=;i<=;i++)
{
for(j=;j<=m;j++) if(i>=s[j]) use[sg[i-s[j]]]=;
for(j=;j<=m;j++) if(!use[j]){ sg[i]=j; break; }
for(j=;j<=m;j++) if(i>=s[j]) use[sg[i-s[j]]]=;
} o=gint();
while(o--) { n=gint();
for(i=;i<=n;i++) a[i]=gint();
for(i=,ans=;i<=n;i++) ans^=sg[a[i]];
if(ans) putchar('W'); else putchar('L'); }
puts(""); }
return ;
}

BZOJ 2940 条纹 (SG函数递推)

稍微复杂了一点,但也没什么好说的,暴力递推SG函数就行了

 #include <cstdio>
#include <cstring>
#include <algorithm>
#define il inline
#define N1 1010
using namespace std;
const int maxn=; template <typename _T> void read(_T &ret)
{
ret=; _T fh=; char c=getchar();
while(c<''||c>''){ if(c=='-') fh=-; c=getchar(); }
while(c>=''&&c<=''){ ret=ret*+c-''; c=getchar(); }
ret=ret*fh;
} int T,n,A,B,C;
int use[N1],sg[N1]; int main()
{
scanf("%d%d%d",&A,&B,&C);
if(A>B) swap(A,B); if(A>C) swap(A,C); if(B>C) swap(B,C);
int i,j,k;
for(i=A;i<=maxn;i++)
{
for(j=;j+A<=i;j++) use[sg[j]^sg[i-j-A]]=;
for(j=;j+B<=i;j++) use[sg[j]^sg[i-j-B]]=;
for(j=;j+C<=i;j++) use[sg[j]^sg[i-j-C]]=;
for(j=;j<=maxn*maxn;j++) if(!use[j]){ sg[i]=j; break; }
for(j=;j+A<=i;j++) use[sg[j]^sg[i-j-A]]=;
for(j=;j+B<=i;j++) use[sg[j]^sg[i-j-B]]=;
for(j=;j+C<=i;j++) use[sg[j]^sg[i-j-C]]=;
}
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
if(sg[n]) puts(""); else puts("");
//printf("%d\n",f[n]);
}
return ;
} /* */

一个不错的模型转化

POJ 1704 Georgia and Bob (阶梯博弈)

题目大意:略

把相邻两个数的距离的差值-1看成一堆石子,第一堆石子数是第一个数到1的距离

那么把第i个数向左移x格相当于把x个石子从第i堆放到第i+1堆里,而挪第n堆的石子则是把石子移出游戏

游戏结束状态就是所有的石子都移出了游戏

发现从右往左数的偶数堆(第2,4,6..堆)的石子是无意义的,因为先手挪,后手就跟着挪,先手只会输

我们只考虑从右往左数的奇数堆,先手把石子从奇数堆移动到了偶数堆,相当于把这些石子移除游戏,也就是删除了奇数堆中的这些石子!

问题转化成了NIM游戏!我们只对从右往左数的奇数堆讨论即可

 #include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N1 10010
#define ll long long
#define ull unsigned long long
using namespace std; const int inf=0x3f3f3f3f;
int gint()
{
int ret=,fh=;char c=getchar();
while(c<''||c>''){if(c=='-')fh=-;c=getchar();}
while(c>=''&&c<=''){ret=ret*+c-'';c=getchar();}
return ret*fh;
} int n,m,T,A,B,de;
int a[N1],sg[N1]; int main()
{
scanf("%d",&T);
while(T--){ int i,j,ans=;
scanf("%d",&n);
for(i=;i<=n;i++) a[i]=gint();
sort(a+,a+n+);
for(i=;i<=n;i++) sg[n-i+]=a[i]-a[i-]-; sg[n]=a[]-;
for(i=;i<=n;i+=) ans^=sg[i];
if(!ans) puts("Bob will win");
else puts("Georgia will win"); }
return ;
}

很多问题里状态很大,我们不能预处理出SG函数值,只能打表找规律

博弈问题的精髓是打表!

HDU 3032 Nim or not Nim (SG函数打表)

题目大意:NIM游戏,每次操作还可以把一堆石子分成两堆,问谁赢

先预处理出单独一堆石子时的sg函数值。

那么,该游戏的sg函数值=每堆石子的sg函数值的异或和

暴力枚举后继状态,打个表找规律就行了

 #include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N1 1000050
#define ll long long
#define dd double
using namespace std;
const dd eps=1e-; template <typename _T> void read(_T &ret)
{
ret=; _T fh=; char c=getchar();
while(c<''||c>''){ if(c=='-') fh=-; c=getchar(); }
while(c>=''&&c<=''){ ret=ret*+c-''; c=getchar(); }
ret=ret*fh;
} int T,n;
int a[N1],sg[N1],use[N1];
const int maxn=; int main()
{
scanf("%d",&T);
while(T--){ scanf("%d",&n);
int i,j,sum=;
for(i=;i<=n;i++)
{
read(a[i]);
if(a[i]%==) sum^=a[i]+;
else if(a[i]%==) sum^=a[i]-;
else sum^=a[i];
}
if(!sum) puts("Bob"); else puts("Alice"); }
return ;
} /*
scanf("%d",&n);
int i,j;
//for(i=1;i<=n;i++) scanf("%d",&a[i]);
sg[0]=0; sg[1]=1;
for(i=2;i<=n;i++)
{
use[sg[0]]=1;
for(j=1;j<i;j++) use[sg[j]]=1, use[sg[j]^sg[i-j]]=1;
for(j=0;j<=i*2;j++) if(!use[j]){ sg[i]=j; break; }
use[sg[0]]=0;
for(j=1;j<i;j++) use[sg[j]]=0, use[sg[j]^sg[i-j]]=0;
}
//for(i=0;i<=n;i++) printf("%d:%d\n",i,sg[i]);
for(i=1;i<=n;i++) printf("%d\n",sg[i]);
*/

HDU 4644 Triangulation (SG函数打表)

题目大意:圆上有$a_{i}$个点,两个人玩游戏,轮流在这些点之间连边,边和边不能交叉,现在有n个圆,问谁赢

和上面的题一模一样的套路,每次连线都会产生两个子游戏,先写暴力打表,然后找规律

规律比较丧病

 #include <cstdio>
#include <cstring>
#include <algorithm>
#define il inline
#define N1 1000010
using namespace std; template <typename _T> void read(_T &ret)
{
ret=; _T fh=; char c=getchar();
while(c<''||c>''){ if(c=='-') fh=-; c=getchar(); }
while(c>=''&&c<=''){ ret=ret*+c-''; c=getchar(); }
ret=ret*fh;
} int T,n;
int a[N1];
int sg1[]={,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,};
int sg2[]={,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,}; int main()
{
scanf("%d",&T);
while(T--) { scanf("%d",&n);
int i,j,sum=;
for(i=;i<=n;i++)
{
read(a[i]);
if(a[i]<=) sum^=sg1[a[i]-];
else sum^=sg2[(a[i]-)%];
}
if(sum) puts("Carol"); else puts("Dave"); }
return ;
} /* */

SG组合游戏变形

气氛变得怪异起来

一些SG组合游戏的终止状态比较特殊,我们可以通过转化解决

POJ 3537 Crosses and Crosses

题目大意:给出一个1*n的网格,一开始全都是白格子,两个人轮流把一个白格子涂黑,谁先涂出来连续3个黑格子谁就赢了

游戏的结束状态不容易直接搞啊

我们剖析游戏本身的性质

先手把一个格子涂黑后,它左右一共连续5个格子(边界另外讨论)一定不能被后手涂

相当于每次涂黑一个格子,删掉连续的不超过5个格子,两侧剩下的格子构成了一个或两个子游戏

依次求出长度为1~n的连续格子的游戏的SG函数即可

 #include <queue>
#include <cmath>
#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N1 2010
#define M1 200010
#define ll long long
#define dd double
using namespace std;
const dd eps=1e-; int n,tl;
int sg[N1],use[N1],que[N1]; int main()
{
scanf("%d",&n);
if(n==){ puts(""); return ; }
if(n==){ puts(""); return ; }
if(n==){ puts(""); return ; }
if(n==){ puts(""); return ; }
sg[]=; sg[]=sg[]=sg[]=; sg[]=;
int i,j;
for(i=;i<=n;i++)
{
que[++tl]=sg[i-]; que[++tl]=sg[i-];
for(j=;j+<=i;j++) que[++tl]=sg[j]^sg[i-j-];
for(j=;j<=tl;j++) use[que[j]]=;
for(j=;j<=i;j++) if(!use[j]){ sg[i]=j; break; }
while(tl) use[que[tl--]]=;
}
if(sg[n]>) puts("");
else puts("");
return ;
}

POJ 2311 Cutting Game

题目大意:给出一张n*m的纸,每次可以把它剪成两半,先剪出来1*1小纸片的人赢

虽然1*1是必败局面,但并不容易直接推出其他格子的SG函数

显然x>1时,1*x的局面先手必胜。而2*2局面必败,进而可以推出2*3,3*3也都是必败局面

利用这两点就可以轻松推出整张纸的SG函数了

 #include <queue>
#include <cmath>
#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N1 205
#define M1 200010
#define ll long long
#define dd double
using namespace std;
const dd eps=1e-; int T,n,m,de;
int sg[N1][N1],use[N1*]; int main()
{
int i,j,k,x,y,ans,flag;
for(i=;i<=;i++) sg[][i]=;
for(i=;i<=;i++) sg[i][]=;
for(i=;i<=;i++) for(j=;j<=;j++) //if(i+j>4)
{
for(k=;k<j-;k++) use[sg[i][k]^sg[i][j-k]]=; //if(i+k>=4&&i+j-k+1>=4)
for(k=;k<i-;k++) use[sg[k][j]^sg[i-k][j]]=; //if(j+k>=4&&j+i-k+1>=4) for(k=;k<=;k++) if(!use[k]){ sg[i][j]=k; break; } for(k=;k<j-;k++) use[sg[i][k]^sg[i][j-k]]=; //if(i+k>=4&&i+j-k+1>=4)
for(k=;k<i-;k++) use[sg[k][j]^sg[i-k][j]]=; //if(j+k>=4&&j+i-k+1>=4)
}
while(scanf("%d%d",&n,&m)!=EOF)
{
if(sg[n][m]) puts("WIN");
else puts("LOSE");
}
return ;
}

BZOJ 1457 棋盘游戏

题目大意:给出一个坐标系,上面有很多个皇后,皇后只能向左/向下/向左下走,两个人轮流每次选择一个皇后移动,谁先把皇后移动到(0,0)谁赢

如果直接把(0,0)当做游戏终止局面的话,求解的问题就是谁先把所有皇后都移动到(0,0)谁赢了

显然如果存在x=y||x=0||y=0的皇后,先手必胜

所以两个人都极力避免自已移动出来上述三种情况的皇后

而皇后只能向左下移动,最终皇后一定集中在(1,2)和(2,1)

我们把SG函数为0的位置设为(1,2)和(2,1)即可

 #include <queue>
#include <cmath>
#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N1 105
#define M1 200010
#define ll long long
#define dd double
using namespace std;
const dd eps=1e-; int T,n;
int sg[N1][N1],use[N1]; int main()
{
int i,j,k,x,y,ans,flag;
for(i=;i<=;i++) for(j=;j<=;j++) if(i!=j)
{
for(k=;k<j;k++) if(k!=i) use[sg[i][k]]=;
for(k=;k<i;k++) if(k!=j) use[sg[k][j]]=;
for(k=;k<min(i,j);k++) use[sg[i-k][j-k]]=; for(k=;k<;k++) if(!use[k]){ sg[i][j]=k; break; } for(k=;k<j;k++) if(k!=i) use[sg[i][k]]=;
for(k=;k<i;k++) if(k!=j) use[sg[k][j]]=;
for(k=;k<min(i,j);k++) use[sg[i-k][j-k]]=;
}
scanf("%d",&T);
while(T--)
{
scanf("%d",&n); ans=,flag=;
for(i=;i<=n;i++)
{
scanf("%d%d",&x,&y);
if(x==y||!x||!y) flag=;
ans^=sg[x][y];
}
if(ans||flag) puts("^o^");
else puts("T_T");
}
return ;
}

还有一些更加奇怪的变形..

POJ 3480 John (anti-SG组合游戏)

题目大意:$n$堆石子的$NIM$游戏,改成取走最后一个石子的人输,问谁赢

先求出该游戏的$sg$函数

<1>$sg$函数为0,且每堆石子数量都是1,显然先手必胜

<2>$sg$函数为1,且每堆石子数量都是1,显然先手必败

那单堆石子数量>1的情况呢?

<3>$sg$函数为0,且存在一堆石子数量>1,先手必败

(1)先手取走了一个石子的石子堆,把$sg$函数变成1

显然后手一定能把$sg$函数还原成0

最后一定会还剩下至少2个"石子数量>1的堆",且此时$sg$函数值为0

不论先手怎么取,sg函数都会变得>1

$NIM$游戏具有对称性!

即对于一个$sg$函数值为0的局面而言,不论先手如何操作,后手都能把$sg$函数调成0

而这种情况下,一定存在石子数>1的堆,所以后手肯定能保持$sg$函数值为1!

最后会剩下一个石子被先手取走,后手赢

(2)先手取走了石子数量>1的石子堆中的任意数量个,把$sg$函数变成>1

此时一定还剩下"石子数量>1的堆",后手也能把$sg$函数调成1

先手如果取单个石子的堆,后手也跟着取,保持$sg$函数值为1即可

<4>$sg$函数为>1,且存在一堆石子数量>1,先手必胜

先手把局面调成<3>就能让对手必败了

 #include <cstdio>
#include <cstring>
#include <algorithm>
#define N1 5050
using namespace std; int n,m,T; template <typename _T> void read(_T &ret)
{
ret=; _T fh=; char c=getchar();
while(c<''||c>''){ if(c=='-') fh=-; c=getchar(); }
while(c>=''&&c<=''){ ret=ret*+c-''; c=getchar(); }
ret=ret*fh;
} int a[N1]; int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
int i,sum=,ma=;
for(i=;i<=n;i++) read(a[i]), sum^=a[i], ma=max(ma,a[i]);
if(ma==){
if(sum) puts("Brother");
else puts("John");
}else{
if(sum) puts("John");
else puts("Brother");
}
}
return ;
}

HDU 3595 GG and MM (every-SG组合游戏)

题目大意:两个人玩游戏,每个子游戏有两堆石子,设石子较少那一堆数量为$x$,那么当前操作的人要在较多的那一堆中取走$kx$个,$k$是正整数且$kx$不能超过石子数量。两个人必须轮流对每一个能操作的子游戏进行操作,结束最后一个游戏的人获胜。

我们希望必胜的游戏一直保持下去,必败的局面早点结束

先处理出每种子游戏的$sg$函数

定义$step$函数表示先手令该游戏结束的最优步数,必胜局面取最大步数,必败局面取最小步数,可得

$step[u]=$

$0\;(sg[u]=0)$

$min(step[v])+1\;(sg[u]=0,sg[v]>0)$

$max(step[u])+1\;(sg[u]>0,sg[v]=0)$

那么先手必胜当且仅当单一游戏中最大的$step$为奇数

这里的$sg$函数的用途是分析局面何时结束,$sg$函数本身并不能决定胜负

 #include <cstdio>
#include <cstring>
#include <algorithm>
#define il inline
#define N1 1010
using namespace std;
const int maxn=;
const int inf=0x3f3f3f3f; template <typename _T> void read(_T &ret)
{
ret=; _T fh=; char c=getchar();
while(c<''||c>''){ if(c=='-') fh=-; c=getchar(); }
while(c>=''&&c<=''){ ret=ret*+c-''; c=getchar(); }
ret=ret*fh;
} int T,n,A,B,C,de;
int use[N1*N1],sg[N1][N1],step[N1][N1]; int main()
{ int i,j,k,w,ans,a,b;
for(i=;i<=maxn;i++)
for(j=;j<=i;j++)
{
if(i==&&j==)
de=;
for(k=j;k<=i;k+=j) use[sg[max(i-k,j)][min(i-k,j)]]=;
for(k=;k<=maxn*maxn;k++) if(!use[k]){ sg[i][j]=k; break; }
if(!sg[i][j]) step[i][j]=inf;
for(k=j;k<=i;k+=j)
{
a=max(i-k,j), b=min(i-k,j);
if(!sg[i][j]&&sg[a][b]) {
step[i][j]=min(step[i][j],step[a][b]);
}else if(!sg[a][b]){
step[i][j]=max(step[i][j],step[a][b]);
}
use[sg[a][b]]=;
}
step[i][j]++;
} while(scanf("%d",&n)!=EOF) { ans=;
for(i=;i<=n;i++)
{
scanf("%d%d",&A,&B);
if(A<B) swap(A,B);
ans=max(ans,step[A][B]);
}
if(ans&) puts("MM"); else puts("GG"); }
return ;
} /* */

BZOJ 1393 Knight (every-SG组合游戏+打表)

打表没商量,思路和上一题差不多

这道题也启示我们一个技巧,辅助函数(本题中的$step$函数)和$sg$函数之间可能存在某种神♂秘的关系,如果表本身的规律不够明显,尝试分类讨论打表

例如此题中,$sg$函数值为0的状态step函数很有规律。而$sg$值不为0的状态的$step$函数,可以根据$sg$函数值为0的后继状态推出来

 #include <cstdio>
#include <cstring>
#include <algorithm>
#define il inline
#define N1 200010
using namespace std;
const int maxn=;
const int inf=0x3f3f3f3f; template <typename _T> void read(_T &ret)
{
ret=; _T fh=; char c=getchar();
while(c<''||c>''){ if(c=='-') fh=-; c=getchar(); }
while(c>=''&&c<=''){ ret=ret*+c-''; c=getchar(); }
ret=ret*fh;
} int m,n,de; inline int check(int x,int y)
{
if(x==n||y==n)
{
if(x==n) swap(x,y);
if(n%==){ if(x==n) return ; return ; }
if(n%==){ if(x==n-) return ; return ; }
if(n%==){ if(<=x%&&x%<=) return ; return ; }
if(n%==){ return ; }
}
if( <=x% && x%<= && <=y% && y%<= )
return ;
return ;
}
inline int query(int x,int y)
{
if(x==n||y==n)
{
if(x==n) swap(x,y);
if(n%==)
{
if(x<=) return n/-;
if(x==n) return n/-+((x-)/)*+;
return n/-+(x/)*;
}
if(n%==)
{
if(x==n) return n-;
if(x==n-) return n-;
return n/+(x-)/*;
}
if(n%==)
{
return n/-+(x-)/;
}
if(n%==)
{
return n/+x/*;
}
}
if(!check(x,y)) return (x/+y/)*;
int ans=;
if(<=x- && y+<=n && !check(x-,y+)) ans=max(ans,query(x-,y+)+);
if(<=x- && y->= && !check(x-,y-)) ans=max(ans,query(x-,y-)+);
if(<=x- && <=y- && !check(x-,y-)) ans=max(ans,query(x-,y-)+);
if(x+<=n && <=y- && !check(x+,y-)) ans=max(ans,query(x+,y-)+);
return ans;
}
int xx[N1],yy[N1],step[N1];
void solve()
{
int i,j,x,y,ma,tmp;
for(j=,ma=;j<=m;j++)
{
scanf("%d%d",&xx[j],&yy[j]);
ma=max(ma,query(xx[j],yy[j]));
}
if(ma&){ puts("YES");
for(j=;j<=m;j++)
{
x=xx[j]; y=yy[j]; tmp=query(x,y);
if(<=x- && y+<=n) if(tmp==query(x-,y+)+){ printf("%d %d\n",x-,y+); continue; }
if(<=x- && y->=) if(tmp==query(x-,y-)+){ printf("%d %d\n",x-,y-); continue; }
if(<=x- && <=y-) if(tmp==query(x-,y-)+){ printf("%d %d\n",x-,y-); continue; }
if(x+<=n && <=y-) if(tmp==query(x+,y-)+){ printf("%d %d\n",x+,y-); continue; }
} }else puts("NO");
} int main()
{
int i,j,k,w,ans,x,y;
scanf("%d%d",&m,&n);
//if(n<=100) S1::solve(); else S2::solve();
solve();
return ;
}

博弈论题目总结(二)——SG组合游戏及变形的更多相关文章

  1. 【博弈论】组合游戏及SG函数浅析

    目录 预备知识 普通的Nim游戏 SG函数 预备知识 公平组合游戏(ICG) 若一个游戏满足: 由两名玩家交替行动: 游戏中任意时刻,合法操作集合只取决于这个局面本身: 若轮到某位选手时,若该选手无合 ...

  2. 组合游戏 - SG函数和SG定理

    在介绍SG函数和SG定理之前我们先介绍介绍必胜点与必败点吧. 必胜点和必败点的概念:        P点:必败点,换而言之,就是谁处于此位置,则在双方操作正确的情况下必败.        N点:必胜点 ...

  3. HDU 1536 S-Nim (组合游戏+SG函数)

    题意:针对Nim博弈,给定上一个集合,然后下面有 m 个询问,每个询问有 x 堆石子 ,问你每次只能从某一个堆中取出 y 个石子,并且这个 y 必须属于给定的集合,问你先手胜还是负. 析:一个很简单的 ...

  4. 浅谈公平组合游戏IGC

    浅谈公平组合游戏IGC IGC简介 一个游戏满足以下条件时被叫做IGC游戏 (前面三个字是自己YY的,不必在意) 竞争性:两名玩家交替行动. 公平性:游戏进程的任意时刻,可以执行的操作和操作者本人无关 ...

  5. 博弈论 && 题目

    终于我也开始学博弈了,说了几个月,现在才学.学多点套路,不深学.(~~) 参考刘汝佳蓝书p132 nim游戏: 假设是两维的取石子游戏,每次可以在任意一堆拿任意数量个(至少一根,因为这样游戏的状态集有 ...

  6. Nim游戏(组合游戏Combinatorial Games)

    http://baike.baidu.com/view/1101962.htm?fr=aladdin Nim游戏是博弈论中最经典的模型(之一),它又有着十分简单的规则和无比优美的结论 Nim游戏是组合 ...

  7. Codeforces 918D MADMAX 图上dp 组合游戏

    题目链接 题意 给定一个 \(DAG\),每个边的权值为一个字母.两人初始各占据一个顶点(可以重合),轮流移动(沿着一条边从一个顶点移动到另一个顶点),要求每次边上的权值 \(\geq\) 上一次的权 ...

  8. 【Visual C++】游戏开发五十六 浅墨DirectX教程二十三 打造游戏GUI界面(一)

    本系列文章由zhmxy555(毛星云)编写,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/16384009 作者:毛星云 ...

  9. Chomp!游戏 (组合游戏Combinatorial Games)

    http://blog.csdn.net/acdreamers/article/details/17021095 有一个n*m的棋盘,每次可以取走一个方格并拿掉它右边和上面的所有方格.拿到左下角的格子 ...

随机推荐

  1. joinColumns和inverseJoinColumns的使用方法

    近期在工作中使用springside.里面用到了hibernate的多对多 一開始我在配置department(部门表)和menu(栏目表)的时候.这样写的. Department实体类中的getMe ...

  2. Android 实现文字与图片的混排

    在我们的项目中,常常会碰到图片与文字混排的问题.解决这类问题的方法有非常多,本文给出的方法不是唯一的.仅仅有依据实际场景才干找到更适合的方法. 本文主要通过xml布局来实现图片与文字的混排(水平排列) ...

  3. Openwrt中luci配置页面cbi小记

    先看看network的配置文件: config interface 'loopback' option ifname 'lo' option proto 'static' option ipaddr  ...

  4. @Component注解

    @component (把普通pojo实例化到spring容器中,相当于配置文件中的 <bean id="  " class="   "/>)泛指各 ...

  5. Codeforces Round #330 (Div. 2)B. Pasha and Phone 容斥

    B. Pasha and Phone   Pasha has recently bought a new phone jPager and started adding his friends' ph ...

  6. C++对象内存布局 (一)

    一.前言 在看了皓哥的C++对象的布局之后:http://blog.csdn.net/haoel/article/details/3081328.感觉自己还是总是不能记得很清楚,故在此总结一下C++对 ...

  7. hdu 1874(最短路 Dilkstra +优先队列优化+spfa)

    畅通工程续 Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submi ...

  8. 【概念的辨异】—— ISO C 与 POSIX C(C standard library 与 C POSIX library)

    ISO C 表示 C Standard Library,也就是 C 标准库. 二者的主要区别在于: POSIX 是 C 标准库的超集(也即是从内容上,C 标准库是 POSIX 库的一部分,POSIX ...

  9. 3.3 文件I/O

    错误的解决方法请参考:http://liangruijun.blog.51cto.com/3061169/673776 3.3.2 访问手机中的存储文件夹 3.3.3 读取assets中的文件 pac ...

  10. diff比较两个文件的差异

    1.diff -ruN a.txt b.txt>patch.txt比较第二个文件与第一个文件相比的变化,并将变化添加到patch.txt文件中,-表示删除的行,+表示添加的行 2.下面的,“&l ...