HELLO WORLD--一起加油(🍺)!|

kingwzun

园龄:3年6个月粉丝:111关注:0

📂数学
🔖博弈
2022-09-10 22:09阅读: 405评论: 0推荐: 1

博弈论 _ SG函数

定义

SG函数是指:在有向图中,对于每个节点x,设从 x 出发共有k条有向边(直接相连的边),分别达到节点y1y2yk
定义SG(x)x的后继节点的SG值构成的集合执行mex()运算后的值

mex()设集合S是一个非负整数集合,mex(S)就是求不属于S的最小非负整数。

例如下图:
image

性质及定理

性质:

  1. SG(i)=k,则i最大能到达的点的SG值为k−1。
  2. 非0可以走向0
  3. 0只能走向非0

定理1:同一个图上
对于一个图GG,如果SG(G)!=0,则先手必胜,反之必败

证明:
若SG(G)=!0
1.根据性质2,先手必可以走向0,
2.因此留给后手的是0,根据性质3,后手只能走向非0
3.以此类推,后手始终无法走向0,后手永远处于非0,当先手到达终点的0时,先手获胜
(由此我们可以知道,有些事是命中注定的~~~)
反之同理,必败

定理2:不同图之间
对于n个图,如果SG(G1)SG(G2)SG(Gn)!=0,则先手必胜,反之必败

证明(类似与Nim游戏):

  1. SG(Gi)=0 时 , xor=0, 显然先手必败
    (PS:结束状态必是状态①,但状态①不一定是结束状态)

  2. xor=x!=0 时,因为肯定存在一个SG(xi)x<SG(xi),而根据SG()的性质1可知,SG(k)可以走到0k1的任何一个状态,
    因此,必定可以从 SG(xi)>SG(xi)x , 于是使得xor=0

  3. xor=0时,当移动任何一个节点时,对应的SG值必然减小,可以证明:xor!=0
    下证:xor!=0
    假设:xor=0xor=0,则说明移动的那个节点的值并没有变化,即从SG(k)变成了kk,但是这与SG函数的性质1相矛盾,因此不成立

证得:若先手面对的状态是xor!=0,则先手方总能使xor=0,即使后手面对的永远是必败态直到结束状态1 ,因此先手必胜!
反之,必败!

模板代码

#include<bits/stdc++.h>
#define int long long
#define pdd pair<double ,double >
using namespace std;
const int N = 110, M = 10010;
int n,m;
int s[N], f[M];
int sg(int x){
if(f[x]!=-1) return f[x];
unordered_set<int> S;
for(int i=0;i<n;i++)
//如果可以减去s[i],则添加到S中
if(x>=s[i]) S.insert(sg(x-s[i]));
//求mex(),即找到最小并不在原集合中的数
for(int i=0; ; i++)
if(!S.count(i)) return f[x]=i;
}
void solve(){
cin>>n;
for(int i=0;i<n;i++) cin>>s[i];
memset(f,-1,sizeof f);
cin>>m;
int ans=0;
while(m--){
int x;cin>>x;
ans^=sg(x);
}
if(ans) puts("Yes");
else puts("No");
}
signed main(){
int t=1;
// cin>>t;
while(t--)
solve();
return 0;
}

模板题

893. 集合-Nim游戏

题意
给定 n 堆石子以及一个由 k 个不同正整数构成的数字集合 S。

现在有两位玩家轮流操作,每次操作可以从任意一堆石子中拿取石子,每次拿取的石子数量必须包含于集合 S,最后无法进行操作的人视为失败。

问如果两人都采用最优策略,先手是否必胜。

输入样例

2
2 5
3
2 4 7

输出样例

Yes

代码

#include<bits/stdc++.h>
#define int long long
#define pdd pair<double ,double >
using namespace std;
const int N = 110, M = 10010;
int n,m;
int s[N], f[M];
int sg(int x){
if(f[x]!=-1) return f[x];
unordered_set<int> S;
for(int i=0;i<n;i++)
//如果可以减去s[i],则添加到S中
if(x>=s[i]) S.insert(sg(x-s[i]));
//求mex(),即找到最小并不在原集合中的数
for(int i=0; ; i++)
if(!S.count(i)) return f[x]=i;
}
void solve(){
cin>>n;
for(int i=0;i<n;i++) cin>>s[i];
memset(f,-1,sizeof f);
cin>>m;
int ans=0;
while(m--){
int x;cin>>x;
ans^=sg(x);
}
if(ans) puts("Yes");
else puts("No");
}
signed main(){
int t=1;
cin>>t;
while(t--)
solve();
return 0;
}

作者:Anoxia_3
链接:https://www.acwing.com/solution/content/13191/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

本文作者:kingwzun

本文链接:https://www.cnblogs.com/kingwz/p/16679003.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   kingwzun  阅读(405)  评论(0编辑  收藏  举报
历史上的今天:
2021-09-10 21/9/9补题 [ 欧拉筛] [数组去重] | [进制转换]
2021-09-10 Java入门
2021-09-10 JAVA:类和对象基础
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起