博弈论进阶(3)

博弈论进阶EverySG

博弈“你那样看着我,就像你真的爱过我一样。”

SG函数拓展Every-SG

一、定义

相对于之前的SG游戏,这一次有了新的拓展。
我们给若干堆石子儿,再一次决策中如果一堆石子还可以取,那么我们就必须取。
定义如下:

EverySG游戏规定,对于还没有结束的单一游戏,游戏者必须对该游戏进行一步决策;
EverySG游戏的其他规则与普通 SG 游戏相同

二、分析

对于这样的游戏,很显然我们会有这样的思路:
对于每一个单一的游戏 ,只需要让我们必胜的单一游戏尽可能玩的结束,必败的单一游戏尽早结束。

顺着这个思路想下去,我们可以做一个新的函数step用于记录一个点最快或者最慢需要多少步走到终点。
当前状态节点为u,后继结点为v
如果u是结束状态,step(u)=0;
对于必胜游戏,也就是SG(u)0的点,我们找到最慢的方式,而作为先手所选择的后续移动的状态一定是必败态,故step(u)=max(step(v))+1,v是必败态;
对于必败游戏,即SG(u)=0等于0的点,需要找到最快的移动方式,后续为均为必胜状态,故step(u)=min(step(u))+1

总结一下:

step(u)={0,umax(step(v))+1,u,vmin(step(v))+1,u

定义了这个函数之后,贾志豪大佬给出了一个定理

对于 Every-SG 游戏先手必胜当且仅当单一游戏中最大的 step 为奇
数。

三、证明

这个定理非常好理解,游戏步数等于最多的单一游戏的步数,显然对于这个单一游戏的步数只要是奇数先手就可以赢。

当然这个证明是不严格的,下面就是来自 贾志豪 的严格证明:


我们要证明解法的正确性,需要证明三点:

  • 对于所有的单一游戏,先手必胜状态的 step值为奇数,先手必败状态的 step 值为偶数。
  • 设最大的 stepstepmax ,那么胜手可以保证该单一游戏最少会在 stepmax 步结束。
  • 设最大的 stepstepmax ,那么胜手可以保证所有他必败游戏最多在 stepmax 步结束。

证明一:
利用数学归纳法:

  1. 所有的终止状态的 step 值为 0,为先手必败状态。
  2. 假设状态树中某些状态点已经符合要求。我们找后继状态点已经全部被证明的状态点。
    如果它是先手必败状态,那么它的后继状态都为先手必胜状态,后继状态的 step 值全为奇数,所以该状态点的 step 值为偶数。
    如果它是先手必胜状态,那么它的后继状态中有先手必败状态,且它所有的先手必败的后记状态的 step 值为偶数,所以该状态点的step 值为奇数。

证明二:
我们设 step 最大的单一游戏为 A(如果有多个的话任取一个),最终的胜手为 W
W 总在 A 游戏处在先手必胜状态时去移动 A 的状态,且 W 可以保证他最多将该游戏的 step 值减小 1。①
对手总在 A 游戏处在先手必败状态时去移动 A 的状态,且对手最多将该游戏的 step 值减小 1。②
由①②得,J 可以保证 A 游戏最少会在 stepmax 步结束。

证明三:
我们设 step 最大的单一游戏为A(如果有多个的话任取一个),最终的胜手为 W。我们考虑除 A 外的任意一个他必败的单一游戏 B
J 在每一回合都可以将 Bstep 值减少 1,将 B 带入先手必胜状态,对手必定将 Bstep 至少减少 1,所以 J 可以保证 B 游戏最多会在 stepmax步结束。

四、一道题

GG and MM
题意:一共有n个游戏,每一个游戏有两堆石子,一次移动可以从大的那堆石子里拿小的那堆石子的整数倍的石子。
只要是可以操作的游戏都要进行操作,不能进行操作的人负。

思路:
我们发现数据范围并不大,对于一个单一的游戏可以建一张SG表把所有的状态保留下来,进而使用这张SG表再建立一张step表。最后再使用Every-SG结论即可。

这个子问题的正解为欧几里得博弈,对于每一对石子x,yx<=y】一定有y=kx+r,考虑k的取值,发现k大于等于2我们就可以决定取得次数的奇偶性了,此时必胜。因为x<=y,所以k0,考虑k=1的情况,发现只有一种转移的情况,此时可以参照之前的推理递归解决问题。下面的代码没有实现这一过程,具体实现可以写这一道题P1290

#include <bitset>
#include <vector>
#include <iostream>
#include <cstring>
#include <cmath>
#include <queue>
#include <algorithm>
#include <map>
#include <unordered_map>
using namespace std;
using namespace std;
#define lowbit(x) x&-x
#define ll int
#define pll pair<ll,ll>
#define dob double
#define For(i,s,n) for(ll i = s;i <= n;i++)
#define mem0(a) memset(a,0,sizeof a)
#define gcd(a,b) __gcd(a,b)
#define lcm(a,b) a/gcd(a,b)*b
#define abs(x) x>=0?x:-x
const int N = 1e3+50;
const double eps = 1e-6;
const ll mod =  1000000007;
const ll inf = 1e9+50;
ll sg[N][N],step[N][N];
void init(){
    for(ll i = 0;i <= 1005;i++)sg[0][i] = step[0][i] = 0;//必败态
    for(ll i = 1;i <= 1005;i++){
        for(ll j = i;j <= 1005;j++){
            sg[i][j] = 0;
            for(ll k = 1;k*i<=j;k++){
                ll x = min(i,j-i*k),y = max(j-i*k,i);
                if(!sg[x][y])sg[i][j] = 1;//存在一个必败态,则为必胜态
            }
            if(sg[i][j]){//如果是必胜态
                step[i][j] = -1;
                for(ll k = 1;k*i<=j;k++){
                    ll x = min(i,j-i*k),y = max(j-i*k,i);
                    if(!sg[x][y])step[i][j] = max(step[i][j],step[x][y]);
                }
                step[i][j]++;
            }
            else{
                step[i][j] = inf;
                for(ll k = 1;k*i<=j;k++){
                    ll x = min(i,j-i*k),y = max(j-i*k,i);
                    step[i][j] = min(step[i][j],step[x][y]);
                }
                step[i][j]++;
            }
        }
    }
}
int main(){
    ios::sync_with_stdio(false);
    init();
    ll n;while(cin>>n){
        ll ans = 0;
        for(ll i = 1;i <= n;i++){
            ll x,y;cin>>x>>y;
            if(x>y)swap(x,y);
            ans = max(ans,step[x][y]);
        }
        if(ans&1)puts("MM");
        else puts("GG");
    }
    return 0;
}
posted @   Paranoid5  阅读(70)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示