zoukankan      html  css  js  c++  java
  • 博弈论--SG函数

    定义:给定一个有向图,无出边的点的SG值定义为0,其他点的SG值定义为到不了的最小的自然数

    具体问题:给定一个石子集合M,再给定一个可以取的数的集合N,求先手必胜还是必败。

     所以SG(10) > 0,所以先手必胜。

    证明:

    (1)最终失败态为0

    (2)非零一定可以变成0

    (3)0一定不能走到0

    由SG函数定义可知上述性质皆正确,所以SG(x)>0,则意味着先手必胜

    那么如果有多堆石子怎么办呢?

    可以发现,这个和Nim游戏简直太像了。

    在集合游戏中SG(x)=k,代表它可以走向{0,1...k-1}中的和状态

    再Nim游戏中,一堆石子x也是可以可以走向小于x的任何状态的

    所以解决方法也是类似的,直接将每个集合的SG函数异或起来,异或值等于res,如果大于0则是先手必胜,否则先手必败。

    证明:

    需要证明(1)终点态res=0

        (2)非零态必然可以转移到0态

        (3)0态不可能转移到0态

                                                              

     1 #include<algorithm>
     2 #include<cstring>
     3 #include<unordered_set>
     4 #include<iostream>
     5 using namespace std;
     6 int n,m;
     7 const int N=110,M=10010;
     8 int s[N],f[M];
     9 int sg(int x){
    10     if(f[x]!=-1){
    11         return f[x];
    12     }
    13     unordered_set<int> se;
    14     for(int i=0;i<m;i++){
    15         int sum=s[i];
    16         if(x>=sum)
    17             se.insert(sg(x-sum));
    18     }
    19     for(int i=0;;i++){
    20         if(!se.count(i)){
    21             return f[x]=i;
    22         }
    23     }
    24 }
    25 int main(void){
    26     cin>>m;
    27     for(int i=0;i<m;i++) cin>>s[i];
    28     memset(f,-1,sizeof(f));
    29     int res=0;
    30     cin>>n;
    31     for(int i=0;i<n;i++){
    32         int t;
    33         cin>>t;
    34         res^=sg(t);
    35     }
    36     if(res){
    37         puts("Yes");
    38     }else{
    39         puts("No");
    40     }
    41     return 0;
    42 }

     拆分Nim游戏

    题目:https://www.acwing.com/problem/content/896/

    游戏一定可以终止,因为最大值一直在减小。(严格证明可用数学归纳法--证明n的两个子状态可以终止即可)

    终止态为全零。

    定义  SG(终止)= 0

    所以res=SG(a1)^SG(a2)^...^SG(an)

    若res==0,则先手必败,否则先手必胜。

    而SG(n)=mex(SG(i)^SG(j))    (i<x&&j<=i) ,必须j<=i,因为x=1时SG(x)!= 0

     1 #include<unordered_set>
     2 #include<cstring>
     3 #include<iostream>
     4 using namespace std;
     5 const int N=110;
     6 int f[N];
     7 int sg(int x){
     8     if(f[x]!=-1) return f[x];
     9     unordered_set<int> S;
    10     for(int i=0;i<x;i++){
    11         for(int j=0;j<=i;j++){
    12             S.insert(sg(i)^sg(j));
    13         }
    14     }
    15     for(int i=0;;i++){
    16         if(!S.count(i)){
    17             return f[x]=i;
    18         }
    19     }
    20 }
    21 int main(void){
    22     memset(f,-1,sizeof(f));
    23     int n;
    24     cin>>n;
    25     int res=0;
    26     for(int i=0;i<n;i++){
    27         int x;
    28         cin>>x;
    29         res^=sg(x);
    30     }
    31     if(res) puts("Yes");
    32     else puts("No");
    33     return 0;
    34 }
  • 相关阅读:
    JAVA共通関数文字列の長さを求める
    JAVA共通関数文字コード変換
    JAVA共通関数文字列に空白を追加する
    JAVA共通関数 半角英数字チェック
    JAVA共通関数項目が半角09か判断する
    JAVA共通関数 指定日の曜日を算出する
    linux添加静态路由(rhel5.4)
    ubuntu编译opencapwap报错解决
    Wireshark网络抓包(二)——过滤器
    net.ipv4.tcp_tw_recycle、net.ipv4.tcp_tw_reuse回收tcp连接总结
  • 原文地址:https://www.cnblogs.com/greenofyu/p/14177553.html
Copyright © 2011-2022 走看看