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. Two Heads Are Often Better Than One

    Two Heads Are Often Better Than One Adrian Wible PROGRAMMING REQUIRES DEEP THOUGHT, and deep thought ...

  2. 设置ckeditor的高度

    设置ckeditor的高度 CKEDITOR.replace('text', { filebrowserImageUploadUrl : 'upload_img.do', language : 'zh ...

  3. IO 字节流学习

    复制图片和MP3 import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.Fil ...

  4. UNP(一):网络编程角度下的TCP、UDP协议

    此博文是学习UNP(UNIX Network Programming)后的读书笔记,供以后自己翻阅回想知识. TCP.UDP概述 在前面<计算机网络与TCP/IP>栏目下已经介绍过一些关于 ...

  5. Geeks - Detect Cycle in a Directed Graph 推断图是否有环

    Detect Cycle in a Directed Graph 推断一个图是否有环,有环图例如以下: 这里唯一注意的就是,这是个有向图, 边组成一个环,不一定成环,由于方向能够不一致. 这里就是添加 ...

  6. MTK camera 闪光灯Flashlight驱动调试流程

    MTK camera 闪光灯Flashlight驱动调试流程 分类: MtkDev  |  作者: topicdev 相关  |  发布日期 : 2014-09-26  |  热度 : 153°   ...

  7. bzoj5038 四叶草魔杖

    很有意思的最小生成树啊. 网上的题解大多是状压+最小生成树,经过我的试验,其实只要把每个联通块找出来,一个个做一次就可以了. 放一个状压的.懒得再写一个搜索找联通块 #include<cstdi ...

  8. nested exception is java.lang.NoClassDefFoundError: net/sf/cglib/proxy/CallbackFilter

    转自:https://blog.csdn.net/licheng989/article/details/28929411 在Bean中有代码 public abstract Axe getAxe(); ...

  9. 利用python+tkinter开发一个点名软件

    最近上课学生多名字记不住,名册忘记了带,要点名怎么办,好久没有写代码了,于是自己写了个点名软件,记录下吧,第一次接触TK也不是太熟悉,写的不太好,记录下源代码 以后遇到要写桌面软件还是可以耍耍的. t ...

  10. mvc.global.asax事件

    1.global.asax文件概述 global.asax这个文件包含全局应用程序事件的事件处理程序.它响应应用程序级别和会话级别事件的代码. 运行时, Global.asax 将被编译成一个动态生成 ...