数学-nim博弈、SG函数

公平组合游戏ICG

若一个游戏满足:

  1. 由两名玩家交替行动;
  2. 在游戏进程的任意时刻,可以执行的合法行动与轮到哪名玩家无关;
  3. 不能行动的玩家判负;

则称该游戏为一个公平组合游戏。
NIM博弈属于公平组合游戏,但城建的棋类游戏,比如围棋,就不是公平组合游戏。因为围棋交战双方分别只能落黑子和白子,胜负判定也比较复杂,不满足条件2和条件3。

NIM游戏

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

我们把这种游戏称为NIM博弈。把游戏过程中面临的状态称为局面。整局游戏第一个行动的称为先手,第二个行动的称为后手。若在某一局面下无论采取何种行动,都会输掉游戏,则称该局面必败。

所谓采取最优策略是指,若在某一局面下存在某种行动,使得行动后对面面临必败局面,则优先采取该行动。同时,这样的局面被称为必胜。我们讨论的博弈问题一般都只考虑理想情况,即两人均无失误,都采取最优策略行动时游戏的结果。
NIM博弈不存在平局,只有先手必胜和先手必败两种情况。

先手必胜状态:可以走到某一个必败状态

先手必败状态: 走不到任何一个必败状态

定理: NIM博弈先手必胜,当且仅当 A1 ^ A2 ^ … ^ An != 0

SG函数

Mex运算

取集合中不存在的最小元素

SG函数定义

SG(终点)=0;
S G ( x ) = SG(x)= SG(x)=MEX { S G ( y 1 ) , S G ( y 2 ) , S G ( y 3 ) . . . . . . S G ( y n ) SG(y_1),SG(y_2),SG(y_3)... ...SG(y_n) SG(y1),SG(y2),SG(y3)......SG(yn)}

S G ( x ) = 0 SG(x)=0 SGx=0时,必败,因为其余非0状态都可以一步到达0状态,把这个状态丢给对手

多个有向图问题

每一个有向图对应一个问题,实际中的博弈题往常对应多个有向图
当多个有向图SG函数异或值为0时,必败,反之必胜
S G ( x 1 ) ⨁ S G ( x 2 ) ⨁ S G ( x 3 ) ⨁ . . . . . . ⨁ S G ( x n ) = 0 SG(x_1) \bigoplus SG(x_2) \bigoplus SG(x_3)\bigoplus......\bigoplus SG(x_n)=0 SG(x1)SG(x2)SG(x3)......SG(xn)=0时,必败

例题

acwing893
在这里插入图片描述
code:

#include<bits/stdc++.h>
//#pragma GCC optimize(2)
using namespace std;
#define rep(i,a,n) for (int i=a;i<=n;i++)
#define per(i,a,n) for (int i=a;i>=n;i--)
#define ll long long
inline ll read(){ll x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;}
inline void write(int x){if(x<0) putchar('-'),x=-x;if(x>9) write(x/10);putchar(x%10+'0');}

#define IOS ios_base::sync_with_stdio(0); cin.tie(0);cout.tie(0)
#define ull unsigned long long
#define PII pair<int,int>
#define pb push_back
#define fi first
#define se second
#define all(a) a+1,a+n+1
#define ALL(a) a.begin(),a.end()
// #define debug(a) cout <<#a << "=" << a << endl;
const int INF = 0x3f3f3f3f;
const ll LINF = 1ll<<60;
const int mod=1e9+7;
#define TT int T;cin>>T;while(T--)
void dbg_out() { cerr << endl; }
template<typename Head, typename... Tail> void dbg_out(Head H, Tail... T) { cerr << ' ' << H; dbg_out(T...); }
#define debug(...) cerr << "(" << #__VA_ARGS__ << "):", dbg_out(__VA_ARGS__);

int n,k,f[10010],s[10010];
int sg(int x){
    if(f[x]!=-1) return f[x];
    unordered_set<int> S;
    rep(i,1,k){
        int sum=s[i];
        if(x>=sum) S.insert(sg(x-sum));
    }
    for(int i=0;;i++)
        if(!S.count(i))
            return f[x]=i;
}
int main(){
    cin>>k;
    rep(i,1,k) cin>>s[i];
    memset(f,-1,sizeof f);
    cin>>n;
    int res=0;
    int x;
    rep(i,1,n){
        cin>>x;
        res^=sg(x);
    }
    if(res) cout<<"Yes\n";
    else cout<<"No\n";
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值