博弈游戏结论

博弈论

前言

无证明,纯结论板子

类Chomp博弈

  • 题意:本质上是先手开始时处在能选择后续游戏的先手与后手的状态上
  • 胜负:只有一步操作时,先手按条件判胜负。否则先手必胜

Bash博弈

  • 题意:一堆 \(n\) 个石子,可取出 \(x\sim y\) 个。拿走最后一个石子者获胜。
  • 胜负: 仅当 $n \% (x+y)< x $ 时必败
  • 当胜负条件取反时,仅当 \(n\% (x+y)<= x \land n\% (x+y) != 0\) 时必败

Fibonacci博弈

  • 题意:一堆 \(n\) 个石子,先手者第一次可以取任意多个,但是不能取完,以后每次取的石子数不能超过上次取子数的 \(2\) 倍。取完者胜
  • 胜负:当 \(n\) 是斐波那契数时,先手必败;否则先手必胜

Wythoff博弈

  • 题意:两堆各若干石子,从任意一堆中至少取出一个或者从两堆中取出同样多的石子,规定每次至少取一个,至多不限,最后取光者胜。
  • 胜负:设当前状态为 \((a,b)\)\(a < b\),则当 \(a = \left\lfloor \frac{\sqrt{5} + 1}{2} (b - a) \right\rfloor\) 时,该状态为必败态。
const ld r=(sqrtl(5.0)+1.0)/2.0;
cout<<!(a==(int)(r*(b-a)))<<endl;

Nim游戏

  • 题意:略
  • 胜负:Nim 和为 \(0\) 时,先手必败
  • SG函数:通过将每个子问题抽象为 \(SG\) 函数,并对 \(SG\) 值进行异或运算来判断胜负
  • 一般DFS只在打表解决不了的情况下用,首选打表预处理。

anti-sg

  • 题意:把终态败局改为终态胜局

  • 胜负:先手必胜当且仅当:

    • 游戏总 \(SG\) 不为 \(0\) 且某个子游戏的 \(SG\) 值大于 \(1\)
    • 游戏总 \(SG\)\(0\) 且不存在任何一个子游戏的 \(SG\) 值大于 \(1\)

multi-sg

  • 题意:在符合拓扑原则的情况下,一个单一游戏的后续可以为多个子游戏
  • 模型:有n堆石子,两个人可以从任意一堆石子中拿任意多个石子(不能不拿)或把一堆数量不少于2的石子分为两堆不为空的石子,无合法操作者输。
  • 举例:\(SG(3)\)的后继状态有\({(0),(1),(2),(1,2)}\),他们的SG值分别为 \(\{0,1,2,{SG(1)\ xor\ SG(2) }\}\),因此 \(SG(3)=mex\{0,1,2,3\}=4\)
  • 胜负:与普通 \(SG\) 类似,计算方面要想一想如何高效计算子游戏的\(SG\)的并(到头来还是硬算\(SG\)

every-sg

  • 题意:给定一张无向图,上面有一些棋子,两个顶尖聪明的人在做游戏,每人每次必须将可以移动的棋子进行移动,不能移动的人输
  • 理解:玩家目标实际上变成最后一个子游戏的胜利,那么必胜的子游戏尽可能拖时间,相反,必输的子游戏尽快结束。时间,就是距离游戏结束还有多少回合。必胜找剩余回合数最多的,必败找剩余回合数最少的。因此,用step记录“时间”得到

\(\text{step}(u) = \begin{cases} 0 & u \text{为终止状态} \\ \max \text{step}(v) + 1 & \text{sg}(u) > 0 \text{ 且 } v \text{为} u \text{的后续且 } \text{sg}(v) == 0 \\ \min \text{step}(v) + 1 & \text{sg}(u) == 0 \text{ 且 } v \text{为} u \text{的后续} \end{cases}\)

  • 胜负:当所有子游戏中最大的 \(step\) 为奇数时,先手必胜

阶梯nim游戏

  • 题意:每次可以从第 \(i\) 堆中取石子并将其移到第 \(i-1\) 堆,直到第 \(0\) 堆为止
  • 胜负:若所有奇数位的石子数异或结果不为 \(0\),则先手必胜

题1 发现有点像阶梯博弈,但棋子的性质不好(能向左移动数位),而观察到移动棋子相当于将棋子左边的空格移动到棋子右侧。不妨将两棋子之间的空格数量当作阶梯nim的石头即可求解。

细节在于要排序后倒序异或,因为最后一个棋子的右侧是“取完了的区域”。

Nim-k

  • 题意:最多可以从 \(k\) 堆中同时取石子
  • 胜负:按二进制位统计1的数量,所有位的1的数量模 \(k==0\), 先手必败

树上删边游戏

  • 题意:给定一棵以某点为根的树,每次删除一条边并将与根不相连的部分移除,无法操作者输
  • 规律
    • 叶子节点的 \(SG\) 值为 0;
    • 中间节点的 \(SG\) 值为其所有子节点 \(SG\) 值加 1 后的异或和
  • 胜负:根节点 \(SG\) 值不为 0 时,先手必胜
  • 变种:若树为有环树,通过Fusion定理 (奇偶是判断边的数量)将偶环缩掉,奇环缩成一个新点和新边,连到环上的边 连到 新点上变为树上删边
//无向有环图
vector<int> e[N];
int sg[N],S[N],top;
void dfs(int u,int fa){
    S[++top] = u;
    vis[u] = 1;
    for(int x:e[u]){
        if(x==fa)continue;
        if(vis[x]==-1){
            dfs(x,u);
            if(vis[x])sg[u] ^= sg[x]+1;
        }else if(vis[x]==1){
            int tmp =S[top],cnt=1;
            while(x!=tmp){
                cnt++;
                vis[tmp]=0;
                tmp = S[--top];
            }
            if(cnt%2)SG[x]^=1;
        }
    }
}
void solve(){  
    int n;
    cin>>n;
    memset(vis,-1,sizeof(vis));
    for(int i=1;i<n;i++){
        int x,y;
        cin>>x>>y;
        e[x].push_back(y),e[y].push_back(x);
    }
    dfs(1,0);
    if(sg[1])cout<<"Alice"<<endl;
    else cout<<"Bob"<<endl;
}

二分图博弈

最大匹配一定包含起点先手必胜

翻硬币游戏


题意

在游戏中,N 枚硬币排成一排,部分正面朝上,部分反面朝上。游戏开始时,从左端对硬币按 1 到 n 编号。游戏规则如下:玩家每次翻动硬币时,所翻硬币中最右侧的一枚必须由正面翻为反面。若无法执行操作(即无符合条件的硬币),则该玩家输局。

示例

假设仅允许翻动三枚硬币,则第三枚硬币必须是由正面翻为反面。若当前局面为“正正反”,则无法翻动硬币(因第三枚已是反面)。


一般规律

局面的 sg 值为其每个正面朝上硬币单一存在时 sg 值的异或和(XOR 和)。若异或和非零,则先手必胜;否则必败。


变种规则

以下是不同约束下的变种游戏,各变种中硬币编号从 1 起始(特例会说明):

  1. 每次只能翻一枚硬币

    • 单一正面硬币 sg=1。
    • 局 sg 值分析:正面硬币数为奇数时 sg=1(先手胜),偶数时 sg=0(先手败)。
  2. 每次能翻一枚或两枚硬币

    • 等效于 Nim 游戏,sg[x]=x。
  3. 每次须连续翻 k 枚硬币

    • 等效于巴什博弈,sg[x] 形式为周期序列:000…01(其中 0 的数量为 k−1),循环出现。
  4. 翻一枚硬币后,须翻动其左侧 k 枚中一枚(除非 x≤k)

    • 等效于减法游戏,sg 值周期为 k+1:序列为 1, 2, 3, ..., k, 0, 1, 2, ..., k, 0, ...
  5. 每次须翻两枚硬币,且距离须在 S={1,2,3} 内(硬币序号从 0 起始)

    • sg[x] 序列:0, 1, 2, 3, 0, 1, 2, 3, 0, ...(周期为 4)。
  6. 每次能翻一枚、二枚或三枚硬币(Mock Turtles 游戏)

    • sg 序列:1, 2, 4, 7, 8, 11, 13, 14, 16, 19, 21, 22, 25, 26, 28, ...
    • 相关概念:一个数在二进制中 1 的个数为奇数时称 odious,否则称 evil
    • 规律:若 2x 是 odious,则 sg[x]=2x;若为 evil,则 sg[x]=2x+1。
    • 推广:每次可翻 1 至 k 枚硬币时,sg[1]=1,其余值取集合 mex{前 n 位至多 k 数字异或和}
  7. 每次须翻连续任意枚硬币(至少一枚)(Ruler 游戏)

    • sg[x]=lowbit(x),序列为 1, 2, 1, 4, 1, 2, 1, 8, ...
  8. 每次须翻四枚对称硬币,且最左与最右必须由正面翻为反面(初始两端正面,Grunt 游戏)

    • 等效于 Grundy 游戏变种:

      • n=0,1,2 时为终止局面,sg=0。
      • n≥3 时 sg 序列:0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, ...
      • 规律:sg=max(0,(i−2)/2)

K倍动态减法

  • 题意:有\(n\)个石子,两个游戏者轮流操作,第一个操作的人最多能拿走\(n-1\)个石子,以后,每个游戏者最多能拿走前一个游戏者拿走数目的\(k\)

  • 胜负:

    • \(k=1\),必败态是 \(2^i\)
    • \(k=2\),Fibonacci博弈
    • \(k>2\),类似Fibonacci博弈,把n分解成数列中不连续项的和,如果n就是数列中的数,必败

    必胜时还要求输出第一步取法,按照上文的理解,将\(n\)分解之后,选择最小的一个\(a[i]\)即可(类似选择二进制的最小的\(1\)

    构造的性质:我们用a数组表示要被求的数列,\(b\)数组中的\(b[i]\)保存 \(a[0...i]\) 组合能够构造的最大数字。那么要选用的下一项只能递减 寻找,直到找到\(a[t] * K < a[i]\) ,而\(b[t]\)就是\(a[0...t]\)所能构造的最大数字,再加上\(a[i]\), 即为\(a[0...i]\)能构造的最大数字,于是\(b[i] = b[t] + a[i]\)

const int N = 1e6 + 5;
int k;
int flag = 0;
ll a[N], b[N];
void solve(){
    ll k, s;
    cis >> s >> k;
    a[0] = b[0] = 1;
    ist i = 0, j = 0;
    while (s > a[i]) {
        i++;
        a[i] = b[i - 1] + 1;  //首先求出当前的a数组
        while (a[j + 1] * k < a[i]) j++;
        if (a[j] * k < a[i])
            b[i] = b[j] + a[i];  //然后根据a数组求b数组
        else
            b[i] = a[i];
    }

    if (s == a[i])
        cout<<"Bob"<<endl;
    else{
        ll res = a[i];
        while (s) {
            if (s >= a[i]) s -= a[i];
            res = a[i];
            i--;
        }
        cout<<"Alice"<<' '<<res<<endl;
        //res是Alice的第一步
    }

}

高维nim和

一维:n 堆石子,轮流选一堆拿走任意非零个。
可以看作是\(x\)轴上撒 n 个黑点,其它都是白点,每次选一个黑点 \(a_i\),再选一个 \(x<a_i\),将线段 \(x∼a_i\)两端点颜色取反。

二维:平面上 n个黑点,每次选一个 $(a_i,b_i) $再选一个 \((x<a_i,y<b_i)\),将矩形 \((x,y)∼(ai,bi)\)四个顶点颜色取反。

三维:对应偏序上升到三维,将长方体八个顶点颜色取反。

以此类推,可以发现黑点之间是互不影响的,所以总状态就是黑点的 Nim和。

我们对于一些二维 Nim 游戏(更高维也行),可以拆分成两维单独的 Nim 然后求 Nim 积。

定义二维 Nim 游戏中的 Nim 积:\(x⊗y=mex{(a⊗b)⊕(a⊗y)⊕(x⊗b)∣a∈[0,x),b∈[0,y)}\)
其中 ⊗ 定义为 Nim 积,⊕ 定义为异或。

nim积可以和sg函数一样推广,即\(sg(x) = sg(x1)⊗ sg(x2)... 其中x1,x2...为x\)的后续子游戏

发现满足乘法交换律和乘法分配率, \(0\) 与所有数做$ Nim$ 积仍然为 $0 \(,\) 1$ 仍然是单位元。

定义费马数: \(2^{2^n},n\)为整数

  • 费马数与任意小于它的数的 Nim 积为一般意义下乘法的积,即$ a⊗x=a×x (x<a)$
  • 费马数与自己的 Nim 积为自己的 \(\frac 32\) 倍,即 \(a⊗a=3/2a=a⊕a/2 。\)
  • 费马数内运算封闭(\(2^{32}内只需要处理a_i的32次乘积,并且预处理2的次方即可\)
//预处理
int nim(int x,int y,int p) 
{
    if(x*y<=min(x,y))return x*y;
    int a=x>>p,b=((1ll<<p)-1)&x,c=y>>p,d=((1ll<<p)-1)&y,res=nim(b,d,p>>1);
    return ((nim(a^b,c^d,p>>1)^res)<<p)^nim(nim(a,c,p>>1),(1ll<<p)>>1,p>>1)^res;
}
int p[35];
signed main()
{
	for(int i=1,x=1;i<=32;i++,x<<=1)cout<<(p[i]=nim(x,x,32))<<'\n';
} 
//自身nim积求值
inline int f(int x)
{
    int res=0;
    for(int i=0;i<=31;x>>=1,i++)res^=(p[i]*(x&1));
    return res;
}
//二维
int res[1000][1000]=0;
ull f(ull x, ull y, int p = 32) {
    if (x <= 1 || y <= 1) return x * y;
    if (p < 8 && rem[x][y]) return rem[x][y];
    ull a = x >> p, b = ((1ull << p) - 1) & x, c = y >> p, d = ((1ull << p) - 1) & y;
    ull bd = f(b, d, p >> 1), ac = f(f(a, c, p >> 1), 1ull << p >> 1, p >> 1), ans;
    ans = ((f(a ^ b, c ^ d, p >> 1) ^ bd) << p) ^ ac ^ bd;
    if (p < 8) rem[x][y] = rem[y][x] = ans;
    return ans;
}

小二维nim

  • 题意:平面上\(n\)个黑点,每次选一个 \((a_i,b_i)\) 再选一个 \((x<a_i,y<b_i)\),将\((x,y)\)\((a_i,b_i)\)两个顶点颜色取反。
  • 胜负:拆分成若干个子游戏,$SG(x,y)= x\ xor\ y $; 同普通一样的判断

取左右nim

只能取最左边和最右边的nim

首先设\(L[i][j]\)表示在\([i,j]\)这一段区间的左侧放上一堆数量为\(L[i][j]\)的石子后,先手必败。

同理定义\(R[i][j]\)表示右侧。

\(L(i,j)=\left\{\begin{array}{ll}0, & x=R(i,j-1), \\ x+1, & L(i,j-1) \leq x<R(i,j-1), \\ x-1, & R(i,j-1)<x \leq L(i,j-1), \\ x, & \text { otherwise. }\end{array}\right.\)

\(x\)表示\(a[j]\),区间dp,同理反向可以处理\(R(i,j)\)\(L(2,n)==a[1]\)先手必败

不平等博弈

没怎么看懂详细请见09年方展鹏大佬的论文
比较经典的问题是取黑白块,需要用sn函数解决
sn{左玩家到达的状态|右玩家到达的状态}
多个游戏的解求sn函数和
结论 x>0,先手必胜 ; x<0,后手必胜 ; x=0,后手必胜

double sn[20][20];
double get_sn(int x,int y){
	if(sn[x][y]!=inf) return sn[x][y];
	double l=-inf,r=inf;
	for(int i=1;i<=x/2;i++) l=max(l,get_sn(i,y)+get_sn(x-i,y));
	for(int i=1;i<=y/2;i++) r=min(r,get_sn(x,i)+get_sn(x,y-i));
	if(l<0&&r>0) sn[x][y]=0;
	else if(floor(l+1)>l&&floor(l+1)<r){
		if(l>=0) sn[x][y]=floor(l+1);
		else sn[x][y]=ceil(r-1);
	}
	else{
		int j,k,tmp=1;
		bool ok=0;
		for(k=1;!ok;k++){
			tmp<<=1;
			for(j=floor(l*tmp+1);!ok&&j<tmp*r;j++) if(j>l*tmp&&j<r*tmp){
				ok=1;
				break;
			}
		}
		sn[x][y]=1.0*j/tmp;
	}
	return sn[x][y];
}
posted @ 2025-07-21 02:20  Tenryon  阅读(39)  评论(0)    收藏  举报