博弈论之SG函数

背景介绍

给定 n n n 堆物品,第 i i i 堆物品有 A i A_i Ai 个。两名玩家轮流行动,每次可以任选一堆,取走任意多个物品,可把一堆取光,但是不能不取。取走最后一件物品者获胜。两个人都采取最优策略,问先手是否能必胜

这种游戏叫做NIM博弈,NIM博弈不存在平局,只有先手必胜和先手必败的两种情况。
定理:NUM博弈先手必胜,当且仅当 A 1   x o r   A 2   x o r   . . .   x o r   A n ! = 0 A_1 \ xor \ A_2 \ xor \ ...\ xor\ A_n != 0 A1 xor A2 xor ... xor An!=0

简单证明:显然,所有物品取光是必败态,此时 A 1   x o r   A 2   x o r   . . .   x o r   A n = 0 A_1 \ xor \ A_2 \ xor \ ...\ xor\ A_n = 0 A1 xor A2 xor ... xor An=0,当 A 1   x o r   A 2   x o r   . . .   x o r   A n ! = 0 A_1 \ xor \ A_2 \ xor \ ...\ xor\ A_n != 0 A1 xor A2 xor ... xor An!=0由于每次只能从一堆中选取物品,所以我们总是能取出一定量的石子使得 A 1   x o r   A 2   x o r   . . .   x o r   A n = 0 A_1 \ xor \ A_2 \ xor \ ...\ xor\ A_n = 0 A1 xor A2 xor ... xor An=0,让对手面临这样的状态,所以我们必然能够胜利。

有向图游戏

给定一个有向无环图,图中有一个唯一的起点,在起点上放有一枚棋子。两名玩家交替地把这枚棋子沿有向边移动,每次可以移动一步,无法移动着判负。该游戏被称为有向图游戏。

任何一个公平组合游戏都可以转化为有向图游戏。具体方法是,把每个局面看成图中的一个节点,并且从每个局面中沿着合法行动能够到达的下一个局面连有向边。

Mex运算

S S S 是一个非负整数集合。定义 m e x ( S ) mex(S) mex(S) 为求出不属于集合 S S S 的最小非负整数,即
m e x ( S )   =   min ⁡ x ∈ N ,   x ∉ S { x } mex(S)\ =\ \min_{x\in N,\ x\notin S} \left\{ x\right\} mex(S) = xN, x/Smin{x}

SG函数

在有向图游戏中,对于每个节点 x x x,设从 x x x 出发共有 k k k 条有向边,分别达到节点 y 1 , y 2 , . . . , y k y_1, y_2, ... , y_k y1,y2,...,yk,定义 S G ( x ) SG(x) SG(x) x x x 的后继节点 y 1 , y 2 , . . . , y k y1, y2, ... , y_k y1,y2,...,yk S G SG SG 函数值构成的集合再执行mex运算的结果,即
S G ( x )   =   m e x ( { S G ( y 1 ) ,   S G ( y 2 ) ,   . . .   ,   S G ( y k ) } ) SG(x)\ =\ mex(\{ SG(y1),\ SG\left( y2\right) ,\ ...\ ,\ SG\left( yk\right) \} ) SG(x) = mex({SG(y1), SG(y2), ... , SG(yk)})
整个有向图游戏 G G G S G SG SG 函数值被定义为有向图游戏起点 s s s S G SG SG函数值,即 S G ( G ) = S G ( s ) SG(G) = SG(s) SG(G)=SG(s)

有向图游戏的和

G 1 , G 2 , . . . , G m G_1, G_2, ... , G_m G1,G2,...,Gm m m m 个有向图游戏。定义有向图游戏 G G G , 它的行动规则是任选某个有向图游戏 G i G_i Gi,并在 G i G_i Gi 上行动一步。 G G G 被称为有向图游戏 G 1 , G 2 , . . . , G m G_1, G_2, ... , G_m G1,G2,...,Gm 的和。
有向图游戏的和的 S G SG SG 函数值等于它包含的各个子游戏 S G SG SG 函数值的异或和,即:
S G ( G ) = S G ( G 1 )   x o r   S G ( G 2 )   x o r   . . .   x o r   S G ( G m ) SG(G) = SG(G_1)\ xor \ SG(G_2)\ xor \ ... \ xor \ SG(G_m) SG(G)=SG(G1) xor SG(G2) xor ... xor SG(Gm)

定理
必败点 S G SG SG函数的值等于0
必胜点 S G SG SG函数的值大于0

例题

题目链接

题目大意

给定一张 N×M 的矩形网格纸,两名玩家轮流行动。

在每一次行动中,可以任选一张矩形网格纸,沿着某一行或某一列的格线,把它剪成两部分。

首先剪出 1×1 的格纸的玩家获胜。

两名玩家都采取最优策略行动,求先手是否能获胜。

提示:开始时只有一张纸可以进行裁剪,随着游戏进行,纸张被裁剪成 2,3,… 更多张,可选择进行裁剪的纸张就会越来越多。

解题思路

显然没有玩家会剪出 1 ∗ X 1*X 1X 或者 X ∗ 1 X*1 X1 的情况,在这样的条件下,要想达到 1*1 的网格纸,就必须经历 2*2、3*2、2*3这三种必败态之一,我们把这三种状态作为终止状态。每一次剪纸,会出现两种新的“子剪纸游戏”,我们对者两者的 S G SG SG 值进行 x o r xor xor 运算,可以得到这个行动面临的 S G SG SG 值,所以我们可以得出递推关系
S G ( N , M )   =   m e x ( { S G ( i ,   M )   x o r   S G ( N − i ,   M ) ,   1   ⩽   i   <   N }   ∪   { S G ( N ,   i )   x o r   S G ( N ,   M − i ) ,   1   ⩽   i   <   M } ) SG(N,M)\ =\ mex(\left\{ SG\left( i,\ M\right) \ xor\ SG\left( N-i,\ M\right) ,\ 1\ \leqslant \ i\ <\ N\right\} \ \cup \ \left\{ SG\left( N,\ i\right) \ xor\ SG\left( N,\ M-i\right) ,\ 1\ \leqslant \ i\ <\ M\right\} ) SG(N,M) = mex({SG(i, M) xor SG(Ni, M), 1  i < N}  {SG(N, i) xor SG(N, Mi), 1  i < M})

我们还要注意的一点是,求解的过程中不能先打出 S G SG SG 表,如果我们打了一个200*200的表,但是在询问的过程中, s g [ N ] [ M ] sg[N][M] sg[N][M] 会被 s g [ 200 − N ] [ 200 − M ] sg[200-N][200-M] sg[200N][200M] 所限制,所以我们每次递归求 S G [ N ] [ M ] SG[N][M] SG[N][M] 即可

Code

#include <bits/stdc++.h>
#define ll long long
#define qc ios::sync_with_stdio(false); cin.tie(0);cout.tie(0)
#define fi first
#define se second
#define PII pair<int, int>
#define PLL pair<ll, ll>
#define pb push_back
using namespace std;
const int MAXN = 207;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1e9 + 7;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
    while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
    return x*f;
}
int sg[207][207];
int n, m;
int solve(int x, int y){
	if(sg[x][y] != -1)
		return sg[x][y];
	vector<int> v(MAXN);
	for(int i = 2; x - i >= 2; i++)
		v[solve(i, y) ^ solve(x-i, y)]++;
	for(int i = 2; y - i >= 2; i++)
		v[solve(x, i) ^ solve(x, y-i)]++;
	int p = 0;
	while(v[p]) p++;
	return sg[x][y] = p;
}

int main()
{
	#ifdef ONLINE_JUDGE
    #else
       freopen("in.txt", "r", stdin);
       freopen("out.txt", "w", stdout);
    #endif

	qc;
    int T;    	
    for(int i = 1; i < MAXN; i++)
    	for(int j = 1; j < MAXN; j++)
    		sg[i][j] = -1;
    sg[2][2] = sg[2][3] = sg[3][2] = 0;
    T = 1;
    while(T--){
    	while(cin >> n >> m)
    		if(solve(n, m))
    			cout << "WIN" << endl;
    		else
    			cout << "LOSE" << endl;
    }
	return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值