zoukankan      html  css  js  c++  java
  • 8个常见的硬币博弈的SG值规律

    翻硬币游戏

     $N$枚硬币排成一排,有的正面朝上,有的反面朝上。我们从左开始对硬币按1 到N编号。

    最右边那个硬币的必须是从正面翻到反面,.谁不能翻谁输。

    局面的SG值为局面中每个正面朝上的棋子单一存在时的SG值的异或和

    1.每次只能翻一个硬币,

    显然,每个硬币的SG值为1

    2.每次能翻转一个或两个(不用连续)

    每个硬币的SG值为其从左到右的编号位置,从0开始

    #include <bits/stdc++.h>
    #define ll long long
    #define rep(ii,a,b) for(int ii=a;ii<=b;++ii)
    #define per(ii,a,b) for(int ii=b;ii>=a;--ii)
    #define show(x) cout<<#x<<"="<<x<<endl
    #define show2(x,y) cout<<#x<<"="<<x<<" "<<#y<<"="<<y<<endl
    #define show3(x,y,z) cout<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl
    #define show4(w,x,y,z) cout<<#w<<"="<<w<<" "<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl
    #define show5(v,w,x,y,z) cout<<#v<<"="<<v<<" "<<#w<<"="<<w<<" "<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl
    #define showa(x,a,b) cout<<#x<<": ";rep(i,a,b) cout<<x[i]<<' ';cout<<endl
    using namespace std;//head
    const int maxn=1e5+10,maxm=2e6+10;
    int casn,n,m,k,kase;
    int sg[maxn];
    int getmex(bool vis[]){
      int mex=0;
      while(vis[mex]) ++mex;
      return mex;
    }
    int getsg(int now){
      if(~sg[now]) return sg[now];
      bool vis[now+1]={0};
      rep(i,1,now-1) vis[getsg(i)]=1;
      vis[now]=1;
      return sg[now]=getmex(vis);
    }
    int main(){IO;
      memset(sg,-1,sizeof sg);
      cin>>n;
      getsg(n);
      showa(sg,1,n);
    }
    

     3.每次只能翻转连续k枚硬币

    每个硬币的SG值为0000...100000..1,其中每k个为一个周期,每个周期前k-1个为0

    #include <bits/stdc++.h>
    #define rep(ii,a,b) for(int ii=a;ii<=b;++ii)
    #define per(ii,a,b) for(int ii=b;ii>=a;--ii)
    #define show(x) cout<<#x<<"="<<x<<endl
    #define show2(x,y) cout<<#x<<"="<<x<<" "<<#y<<"="<<y<<endl
    #define show3(x,y,z) cout<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl
    #define show4(w,x,y,z) cout<<#w<<"="<<w<<" "<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl
    #define show5(v,w,x,y,z) cout<<#v<<"="<<v<<" "<<#w<<"="<<w<<" "<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl
    #define showa(x,a,b) cout<<#x<<": ";rep(i,a,b) cout<<x[i]<<' ';cout<<endl
    using namespace std;//head
    const int maxn=1e5+10,maxm=2e6+10;
    int casn,n,m,k,kase;
    int sg[maxn];
    int getmex(bool vis[]){
      int mex=0;
      while(vis[mex]) ++mex;
      return mex;
    }
    int getsg(int now,int k){
      if(~sg[now]) return sg[now];
      bool vis[now+1]={0};
      int flag=0;
      rep(i,now-k+1,now-1){
        flag^=getsg(i,k);
      }
      vis[flag]=1;
      return sg[now]=getmex(vis);
    }
    int main(){IO;
      memset(sg,-1,sizeof sg);
      cin>>n>>k;
      rep(i,0,k-1) sg[i]=0;
      getsg(n,k);
      showa(sg,1,n);
    }
    

    4:每次翻动第X个硬币后,必须翻动其左侧最近K个硬币中的一个,除非X是小于等于3

    SG值为1,2,3,4..K,0,1,2,3,4..K,0每K+1个数字为一个周期

    #include <bits/stdc++.h>
    #define rep(ii,a,b) for(int ii=a;ii<=b;++ii)
    #define per(ii,a,b) for(int ii=b;ii>=a;--ii)
    #define show(x) cout<<#x<<"="<<x<<endl
    #define show2(x,y) cout<<#x<<"="<<x<<" "<<#y<<"="<<y<<endl
    #define show3(x,y,z) cout<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl
    #define show4(w,x,y,z) cout<<#w<<"="<<w<<" "<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl
    #define show5(v,w,x,y,z) cout<<#v<<"="<<v<<" "<<#w<<"="<<w<<" "<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl
    #define showa(x,a,b) cout<<#x<<": ";rep(i,a,b) cout<<x[i]<<' ';cout<<endl
    using namespace std;//head
    const int maxn=1e5+10,maxm=2e6+10;
    int casn,n,m,k,kase;
    int sg[maxn];
    int getmex(bool vis[]){
      int mex=0;
      while(vis[mex]) ++mex;
      return mex;
    }
    int getsg(int now,int k){
      if(~sg[now]) return sg[now];
      bool vis[now+1]={0};
      rep(i,max(0,now-k),now-1){
        vis[getsg(i,k)]=1;
      }
      return sg[now]=getmex(vis);
    }
    int main(){
      memset(sg,-1,sizeof sg);
      cin>>n>>k;
      getsg(n,k);
      showa(sg,1,n);
    }
    

     5.每次可以翻1,2,3..k个硬币

    规律,sg[1]=1,其余值为从前几个中选出至多k个数字异或和的集合mex

    #include <bits/stdc++.h>
    #pragma GCC target ("popcnt")
    #define rep(ii,a,b) for(int ii=a;ii<=b;++ii)
    #define per(ii,a,b) for(int ii=b;ii>=a;--ii)
    #define show(x) cout<<#x<<"="<<x<<endl
    #define show2(x,y) cout<<#x<<"="<<x<<" "<<#y<<"="<<y<<endl
    #define show3(x,y,z) cout<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl
    #define show4(w,x,y,z) cout<<#w<<"="<<w<<" "<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl
    #define show5(v,w,x,y,z) cout<<#v<<"="<<v<<" "<<#w<<"="<<w<<" "<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl
    #define showa(x,a,b) cout<<#x<<": ";rep(i,a,b) cout<<x[i]<<' ';cout<<endl
    using namespace std;//head
    const int maxn=1e5+10,maxm=2e6+10;
    int casn,n,m,k,kase;
    int sg[maxn];
    int getmex(bool vis[]){
      int mex=0;
      while(vis[mex]) ++mex;
      return mex;
    }
    int getsg(int now,int k){
      if(~sg[now]) return sg[now];
      bool vis[now*200]={0};
      int st=(1<<(now-1))-1;
      rep(i,0,st){
        if(__builtin_popcount(i)<k){
          int x=0;
          for(int j=i;j;j&=j-1){
            x^=getsg(__builtin_ffs(j),k);
          }
          vis[x]=1;
        }
      }
      return sg[now]=getmex(vis);
    }
    int main(){
      memset(sg,-1,sizeof sg);
      cin>>n>>k;
      rep(i,1,n)getsg(i,k);
      showa(sg,1,n);
      return 0;
    }
    

     6.每次可以翻1,2,3..k个硬币

    SG[1]=1,其余的SG值为,前面所有的SG值选出至多k个的所有组合的异或和集合的mex

    #include <bits/stdc++.h>
    #pragma GCC target ("popcnt")
    #define rep(ii,a,b) for(int ii=a;ii<=b;++ii)
    #define per(ii,a,b) for(int ii=b;ii>=a;--ii)
    #define show(x) cout<<#x<<"="<<x<<endl
    #define show2(x,y) cout<<#x<<"="<<x<<" "<<#y<<"="<<y<<endl
    #define show3(x,y,z) cout<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl
    #define show4(w,x,y,z) cout<<#w<<"="<<w<<" "<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl
    #define show5(v,w,x,y,z) cout<<#v<<"="<<v<<" "<<#w<<"="<<w<<" "<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl
    #define showa(x,a,b) cout<<#x<<": ";rep(i,a,b) cout<<x[i]<<' ';cout<<endl
    using namespace std;//head
    const int maxn=1e5+10,maxm=2e6+10;
    int casn,n,m,k,kase;
    int sg[maxn];
    int getmex(bool vis[]){
      int mex=0;
      while(vis[mex]) ++mex;
      return mex;
    }
    int getsg(int now,int k){
      if(~sg[now]) return sg[now];
      bool vis[now*1000]={0};
      int st=(1<<(now-1))-1;
      rep(i,0,st){
        if(__builtin_popcount(i)<k){
          int x=0;
          for(int j=i;j;j&=j-1){
            x^=getsg(__builtin_ffs(j),k);
          }
          vis[x]=1;
        }
      }
      return sg[now]=getmex(vis);
    }
    int main(){
      memset(sg,-1,sizeof sg);
      cin>>n>>k;
      rep(i,1,n)getsg(i,k);
      showa(sg,1,n);
      return 0;
    }
    

     7.每次可以翻转连续的任意个硬币

    sg: 1 2 1 4 1 2 1 8 1 2 1 4 1 2 1...

    sg[x]为lowbit(x)

    #include <bits/stdc++.h>
    #define rep(ii,a,b) for(int ii=a;ii<=b;++ii)
    #define per(ii,a,b) for(int ii=b;ii>=a;--ii)
    #define show(x) cout<<#x<<"="<<x<<endl
    #define show2(x,y) cout<<#x<<"="<<x<<" "<<#y<<"="<<y<<endl
    #define show3(x,y,z) cout<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl
    #define show4(w,x,y,z) cout<<#w<<"="<<w<<" "<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl
    #define show5(v,w,x,y,z) cout<<#v<<"="<<v<<" "<<#w<<"="<<w<<" "<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl
    #define showa(x,a,b) cout<<#x<<": ";rep(i,a,b) cout<<x[i]<<' ';cout<<endl
    using namespace std;//head
    const int maxn=1e5+10,maxm=2e6+10;
    int casn,n,m,k,kase;
    int sg[maxn];
    int getmex(bool vis[]){
      int mex=0;
      while(vis[mex]) ++mex;
      return mex;
    }
    int getsg(int now){
      if(~sg[now]) return sg[now];
      bool vis[now*10]={0};
      int flag=0;
      per(i,1,now-1){
        int x=0;
        rep(j,i,now-1) x^=getsg(j);
        vis[x]=1;
      }
      vis[0]=1;
      return sg[now]=getmex(vis);
    }
    inline int lb(int x) {return x&(-x);}
    int main(){
      memset(sg,-1,sizeof sg);
      cin>>n;
      rep(i,1,n)getsg(i);
      showa(sg,1,n);
      cout<<"lb: ";rep(i,1,n) cout<<lb(i)<<' ';
    }
    

    8.每次必须翻转4个对称的硬币,其中最左与最右的硬币都必须是从正翻到反,且初始情况保证1和n为正

    sg: 0 0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9

    规律为max(0,(i-2)/2) 同时也是正多边形中不全等的对角线个数

    注意此时,单个局面为首尾都是正面硬币

    #include <bits/stdc++.h>
    #define rep(ii,a,b) for(int ii=a;ii<=b;++ii)
    #define per(ii,a,b) for(int ii=b;ii>=a;--ii)
    #define show(x) cout<<#x<<"="<<x<<endl
    #define show2(x,y) cout<<#x<<"="<<x<<" "<<#y<<"="<<y<<endl
    #define show3(x,y,z) cout<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl
    #define show4(w,x,y,z) cout<<#w<<"="<<w<<" "<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl
    #define show5(v,w,x,y,z) cout<<#v<<"="<<v<<" "<<#w<<"="<<w<<" "<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl
    #define showa(x,a,b) cout<<#x<<": ";rep(i,a,b) cout<<x[i]<<' ';cout<<endl
    using namespace std;//head
    const int maxn=1e5+10,maxm=2e6+10;
    int casn,n,m,k,kase;
    int sg[maxn];
    int getmex(bool vis[]){
      int mex=0;
      while(vis[mex]) ++mex;
      return mex;
    }
    int getsg(int now){
      if(~sg[now]) return sg[now];
      bool vis[now*100]={0};
      rep(i,1,now){
        if(i>=now-i) break;
        int flag=getsg(now-2*i);
        vis[flag]=1;
      }
      return sg[now]=getmex(vis);
    }
    int main(){
      memset(sg,-1,sizeof sg);
      cin>>n;
      sg[1]=sg[2]=sg[3]=0;
      rep(i,1,n)getsg(i);
      showa(sg,1,n);
      cout<<"fx: ";rep(i,1,n) cout<<max(0,(i-2)/2)<<' ';
    }
    
  • 相关阅读:
    10大开源文档管理系统,知识管理系统
    okhttp原理,okhttp为什么好?
    开放式创新对程序开发有什么深远的影响?
    TypeScript中文手册【从入门到精通】
    CentoOS6 32停更了,如何继续用yum源【解决方案】
    electronic为什么要用JavaScript开发桌面应用
    统一身份认证登录入口,统一用户认证和单点登录解决方案
    PHP数组如何倒叙 array_reverse
    Windows electron开发实例大全
    AI深度学习的基础上处理自然语言
  • 原文地址:https://www.cnblogs.com/nervendnig/p/11650523.html
Copyright © 2011-2022 走看看