zoukankan      html  css  js  c++  java
  • About Gray码

    Gray码:

    先来看这么一个逗比问题:

      一个月黑风高的夜晚,你成功潜入了银行。银行里的保险柜上有n个开关,

    当每个开关都置于正确位置,保险柜会被打开,你的时间非常值钱,请你制定

    一个作战计划,确保$2^n-1$次以内的操作能打开保险柜,不然正义的黑喵

    警长会把你抓走!


    为了逃避黑喵警长的魔爪,我们在这里得使用格雷码。

    格雷码的介绍:
    https://en.wikipedia.org/wiki/Gray_code
    http://baike.baidu.com/item/%E6%A0%BC%E9%9B%B7%E7%A0%81

    下面介绍几种比较实用的姿势。

    1. 二进制 -> Gray

    LL to_gray(LL x)
    {
    return x ^= (x>>1);//返回结果为Gray码的十进制表示。
    }
    

      

    2. Gray -> 二进制

    LL to_bin(LL x)
    {
      LL y = x;
      while(x>>=1)
      {
        y ^= x;
      }
      return y;
    }
    

      

    3. 构造长度为n的格雷码

    构造姿势有很多种,不过直接把0~(1<<n)的二进制数字依次转为
    格雷码是一种很方便的搞法。

    vector<int> G;
    for(int i=0;i<(1<<n);i++)
    {
      G.push_back(to_gray(i));
    }
    

      

    来切两道格雷码的题
    Gym 101170 H

    /*
    求出两个格雷码对应的二进制数的十进制表示
    做差即可。
    */
    
    #include <iostream>
    #include <vector>
    using namespace std;
    typedef long long LL;
    LL n, tmp[62]; 
    char s1[62], s2[62];
    
    LL to_bin(LL x)
    {
        LL y = x;
        while(x>>=1)
        {
            y ^= x;
        }
        return y;
    }
    
    int main()
    {
        scanf("%lld %s %s", &n, s1+1, s2+1);
        LL ans1 = 0;
        for(int i=1;i<=n;i++)
        {
            ans1 += (LL)(s1[i]-'0')*(1LL<<(n-i));//此处有可能爆int
        }
    
        LL ans2 = 0;
        for(int i=1;i<=n;i++)
        {
            ans2 += (LL)(s2[i]-'0')*(1LL<<(n-i));
        }
    
        LL res = to_bin(ans2) - to_bin(ans1) - 1;
        cout << res << endl;
    }
    

      


    Gym 101252 J

    /*
    题目要我们生成组合 && 相邻的两个组合必须很接近由这
    点,我们可以想到Gray码。
    注意到台上的人数不能超过k。这和格雷码说好的不一样啊!
    其实,如果我们把n阶格雷码中,1的个数大于k的元素删除
    就会得到符合要求的序列。比较序列中相邻的两个元素,即
    可得出操作。
    
    为什么放逐掉那些数字,就能得到合法解呢?
    下面粗略地分析了一下这个细思极恐的问题。
    —————————————————昏割线——————————————————————————
    先来看格雷码的另一种构造姿势:
    
    a_{0},a_{1},.....a_{n-1}表示的格雷码的下
    一个元素的构造方法
    
    1. 如果 a_{0}+a_{1}+...+a_{n-1} 为偶数。
    对最低位取反。eg: 101000 -> 101001
    
    2. 如果 a_{0}+a_{1}+...+a_{n-1} 为奇数。
    对位数最低的1的前一位取反。eg:101001 -> 101011
    
    分析一下n=5,k=4的情况。
    11110 舞台上有4个人
    11111 咦!有5个人了,多出来了一个人。
    11101 第1位 与 第0位 发生了交换
    
    再来看看n=5,k=2的情况。
    11000  舞台上有2人
    11001  人多啦!
    11011    
    11010  
    11110  (喵)   
    11111  (喵)
    11101  (喵)
    11100  
    10100  此时,舞台上人数为2。而且是11000的位数最低的1与
    其右边发生了交换。注意到(喵喵喵)那三行。对应的正是n=5,k=4
    的情况,因此该问题是可以通过第二类数学归纳法得到证明。
    
    */
    #include <iostream>
    #include <algorithm>
    #include <vector>
    #include <cstring>
    #include <cstdio>
    using namespace std;
    typedef long long LL;
    vector<int> vc;
    int n, k;
    
    //求出符合要求的序列。
    void to_gray(LL x)
    {
        int cnt = 0, ans = x^(x>>1);
        while(ans)
        {
            if(ans&1) cnt ++;
            ans >>= 1;
        }
        if(cnt > k) return;
        return vc.push_back(x^(x>>1));
    }
    
    //比较序列中相邻的两个元素,并得出该怎样操作。
    void cmp(int x, int y)
    {
        int tmp = x^y;
        int plus = 0, substr = 0;
        for(int i=0;i<n;i++)
        {
            if((1<<i)&tmp)
            {
                if((1<<i)&x)
                {
                    substr = i+1;
                }
                if((1<<i)&y)
                {
                    plus = i+1;
                }
            }
        }
        if(plus && !substr)
        {
            printf("+%d", plus);
        }
        if(!plus && substr)
        {
            printf("-%d", substr);
        }
        if(plus && substr)
        {
            if(plus > substr)
            {
                printf("++%d", substr);
            } else {
                printf("--%d", substr);
            }
        }
    }
    
    int main()
    {
        freopen("input.txt","r",stdin);
        freopen("output.txt","w",stdout);
        cin >> n >> k;
        for(int i=0;i<(1<<n);i++)
        {
            to_gray(i);
        }
        for(int i=1;i<vc.size();i++)
        {
            cmp(vc[i-1], vc[i]);
        }
        printf("-%d", n);
    }
    

      

  • 相关阅读:
    仿网易菜单 实现侧滑 SlidingMenu
    MD5 Util
    Android 关于SD卡、机身内存以及分辨率的转换的工具类
    android TextView 显示图片,类似于聊天窗口。
    WEB显示(隐藏)系统时间
    I/O复习四 字符流 InputStreamReader/OutputStreamWriter
    Knockout应用开发指南(完整版) 目录索引
    C#设计模式(23种设计模式)
    win7+ubuntu 13.04双系统安装方法
    GeoServer地图开发解决方案
  • 原文地址:https://www.cnblogs.com/RUSH-D-CAT/p/6766360.html
Copyright © 2011-2022 走看看