• ACM学习历程—计蒜客15 单独的数字(位运算)


    http://nanti.jisuanke.com/t/15

    题目要求是求出只出现一次的数字,其余数字均出现三次。

    之前有过一个题是其余数字出现两次,那么就是全部亦或起来就得到答案。

    这题有些不太一样。

    显然,最裸的做法就是cnt[i]表示i出现的次数。然后求出cnt[i]1的那一个。

    然后可能会考虑到,对于这些数字的二进制位来看,某一位二进制位出现3的倍数次的话,那么只出现一次的数字这一位一定是0。如果出现3的倍数加1次,那么自然,只出现一次的数字这一位一定是1

    然后就考虑用cnt[i]表示,这些数二进制第i位,出现1的次数。

    最后答案就能用cnt[32]生成了。

    时间复杂度O(nlogx),空间32*int

    其实我们考虑的时候,只需要保存cnt[i]%3的结果,也就是说cnt[i]的取指只有012三种。

    那么我们就可以这样来设定状态,cnt[i]表示:当cnt[i]的某位二进制位出现1时,表示这些数字在这一位出现的次数模3i

    于是就有cnt[1], cnt[2], cnt[3]三种了,而且这三个二进制位的某一个只有一个是1

    接下来考虑状态是怎么转移的。

    加入新加入一个x

    这个x的第j位是1

    那么如果cnt[i-1]的第j位是1cnt[i]的第j位就将要变成1。这是出现进位的情况。

    那么如果cnt[i]的第j为是0,自然,除非发生进位,否则还是0

    如果x的第j位是0

    那么如果cnt[i]的第j位是1,那么还是1

    那么如果cnt[i]的第j为是0,那么还是0

    也就是说,新的cnt[i]将有两部分构成,一部分是原有的1,而且没有进位的,还有一部分是进位得到的1

    对于原有没有进位的1,满足原0,现在还是0;原1,来了1,变成0;原1,来了0,还是1;发现~x&cnt[i]满足这个条件。

    对于进位得到的1,显然是x&cnt[i-1]

    两部分结合cnt[i] = (x&cnt[i-1])&(~x&cnt[i])

    于是可以时间复杂度O(n)实现。

     

    代码:

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    
    using namespace std;
    
    int main()
    {
        int n, x, y, z, t, a;
        while (scanf("%d", &n) != EOF)
        {
            x = y = 0;
            z = ~0;
            for (int i = 0; i < n; ++i)
            {
                scanf("%d", &a);
                t = (a&x)|(~a&y);
                x = (a&z)|(~a&x);
                z = (a&y)|(~a&z);
                y = t;
            }
            printf("%d
    ", x);
        }
    }
    View Code
  • 相关阅读:
    解决在PDF文档中复制代码报错问题
    JAVA高级复习泛型
    SpringBoot高级监听原理
    SpringBoot整合其它框架整合Junit
    SpringBoot高级监控
    JAVA基础复习异常处理
    SpringBoot 整合 webservice 示例
    关于ScrollView的子View无法布满屏幕的问题
    Android开发中头疼的R文件问题
    博客园美化[SimpleMemory主题+tctip插件]
  • 原文地址:https://www.cnblogs.com/andyqsmart/p/5546207.html
Copyright © 2020-2023  润新知