SG函数——简谈

开始学博弈论,主要讨论的是一类组合游戏问题的解法,什么样的问题?总(chao)结(xi)如下:

1.游戏有两个人参与,二者轮流做出决策。且这两个人的决策都对自己最有利。
2.当有一人无法做出决策时游戏结束,无法做出决策的人输。无论二者如何做出决策,游戏可以在有限步内结束。
3.游戏中的同一个状态不可能多次抵达。且游戏不会有平局出现。
4.任意一个游戏者在某一确定状态可以作出的决策集合只与当前的状态有关,而与游戏者无关。

这类游戏称为SG组合游戏,解决这类问题的利器就是SG函数。

预备知识:

状态图:将游戏中的状态可以画成图,每个节点是一个状态,每条边表示从一个状态转移到另一个状态的操作。那么判断这个状态是必胜态(先手存在必胜的策略)还是必败态,有下列规则:
1.如果该状态没有后继,那么它是必败态(SG组合游戏.2);
2.如果该状态至少有一个后继是必败态,那么它是必胜态,反之它是必败态。

游戏的和:考虑任意多个同时进行的 SG-组合游戏,这些 SG-组合游戏的和是这样一个 SG-组合游戏,在它进行的过程中,游戏者可以任意挑选其中的一个单一游戏进行决策,最终,没有办法进行决策的人输。

比如说最典型的游戏模型:nim游戏,规则如下:
1.桌子上有 N 堆石子,游戏者轮流取石子。
2.每次只能从一堆中取出任意数目的石子,但不能不取。
3.取走最后一个石子者胜。
这里每一个石子堆都是1个单一的组合游戏,而这整个游戏,就是多个单一游戏组合起来的游戏的和。
如果计算每一个状态往下,(x1,x2,…xn),复杂度都太高,那么这里就需要先介绍SG函数:

SG函数是对一个游戏中的状态的评估函数(类似于A*),定义SG(x)=mex(S),其中S表示x的所有后继状态的SG值的集合,mex(S)表示集合S中不存在的最小自然数。
特殊的,没有后继的状态的SG值等于0。明显的如果一个状态是必败态,那么它的SG值等于0。
但是这样的话复杂度有点大,所以我们还需要使用SG定理——

考虑一个由k个单一游戏所组成的游戏的和,它的SG值等于它的所有单一游戏的SG值得nim和(异或和)。
这样就可以分别求出k个单一游戏的SG值然后求一趟xor和,还有一件很神奇的事情,就是单堆nim游戏的SG函数满足一个神奇的定律: SG(x)=x,证明略(因为我lan);

至于其他的组合游戏可以尝试转换成nim游戏然后进行再套,如果不行,我也不知道……
模板题:hihocoder 1163 nim裸题(2017.06.13更新)

#include<cstdio>
using namespace std;
int main()
{
    int n,t=0; scanf("%d",&n);
    for (int i=1;i<=n;i++)
    {
      int x;  scanf("%d",&x); t^=x;
    }
    if (t) printf("Alice"); else printf("Bob");
    return 0;
}

好吧,编不下去了,上题目:
洛谷 P1247 取火柴游戏

Nim游戏的加强版,要求求出第一次操作。
首先根据流程,xor,xor,异或出一个值x;现在我们要构造出一个必败态(对对手而言),所以修正之后要求x为0。
异或xor中有一种神奇的规律,x xor x=0(x^x=0),根据这个定理,将所有的数枚举一趟,每次将x^ai得到其余n-1个数的xor和,然后现在就要将ai变成x^ai,不过ai只能变小,所以还是要先判断一次。

这道题

#include<cstdio>
#include<cstring>
using namespace std;
int n,a[500005],x;
inline void readi(int &x){
    x=0; char ch=getchar();
    while ('0'>ch||ch>'9') ch=getchar();
    while ('0'<=ch&&ch<='9') {x=x*10+ch-'0'; ch=getchar();}
}
int main()
{
    readi(n); x=0;
    for (int i=1;i<=n;i++)
    {
        readi(a[i]); x^=a[i];
    }
    if (!x){
        printf("lose");
        return 0;
    }
    for (int i=1;i<=n;i++)
    {
        if((a[i]^x)>=a[i]) continue;
        printf("%d %d\n",(a[i]-(a[i]^x)),i);
        a[i]=a[i]^x;
        break;
    }
    for(int i=1;i<n;i++) printf("%d ",a[i]); printf("%d",a[n]);
    return 0;
}

PS:为什么学博弈论? 好吧,其实真相是我已经被数据结构折磨了一个多月,所以果断……呵呵哒
PPS:Orz Lynstery

番外
菜鸡fhj的莫名其妙的一天
早上在bzoj提交代码,RE;
下午在bzoj提交代码,TLE;
晚上在bzoj提交代码,WA。
相当“充实”的一天呐~~呐 呐~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值