lowbit

求最后的一位10串,假设要求的数为N,N&(-N)的二进制则为最后的1串/10串,lowbit,妙哉。

具体过程:
假设N的二进制是???10000
N反码则是!!!(表示?的取反)01111
N补码也就是-N!!!10000
N&(-N)为00010000,求得最后的10串。

来道题目:
This is the tale of Zephyr, the greatest time traveler the world will never know. Even those who are aware of Zephyr’s existence know very little about her. For example, no one has any clue as to which time-period she is originally from.

But we do know the story of the first time she set out to chart her own path in the time stream. Zephyr had just finished building her time machine which she named - “Dokhina Batash”. She was making the final adjustments for her first trip when she noticed that a vital program was not working properly. The program was supposed to take a number N and find what Zephyr called its Onoroy value.

The Onoroy value of an integer N is the number of ones in its binary representation. For example, the number 13 (11012) has an Onoroy value of 3. Needless to say, this was an easy problem for the great mind of Zephyr. She solved it quickly and was on her way.

You are now given a similar task. Find the first number after N which has the same Onoroy value as N.

Input:
Input starts with an integer T (≤ 65), denoting the number of test cases.

Each case begins with an integer N (1 ≤ N ≤ 109).

Output:
For each case of input you have to print the case number and the desired result.

Sample input:

5
23
14232
391
7
8

Sample output:

Case 1: 27
Case 2: 14241
Case 3: 395
Case 4: 11
Case 5: 16

题意:大致意思就是给定一个数N,找出第一个比N且二进制中1的数量和N相同的数。一开始想着暴力+贪心,从N开始向上找,结果超时了。当然直接贪心也是可以做的,不过分的情况多,略显复杂。
无奈网上搜题解,学了一波位运算神操作。
https://blog.csdn.net/chen_logos/article/details/45869997参考本篇文章。
主要思路如下:
因为要找大的,手写几个数01111->10111,10101->10110,1111->10111,发现基本规律是让第一个01组合变成10,然后后面所有的1拿出来,从右往左放入,比如01111->10 000先01变10,再把后面3个1全放进去,得10111。
这里让10变01就要用到lowbit操作,对于为零的整数,N&(-N)的值就是将其最低位的1独立出来后的值。这是由于计算机中负数采用二进制补码表示,-N实际上对应于(~N)+1(将N按位取反然后加上1)。
N=011110
x=N&(-N)=10(第一个10串,虽然没有0)
y=N+x=100000(让后面连续的1进位改为0,前面的01变10)
z=N&(~y)=011110=11110(会留下第一个连续1的片段),这一步理解了你就超神了(这一步我搞了了两小时才彻底弄清楚TwT)。首先明确这一步的目的是,找第一个连续1片段,去掉一个1,再从右到左放入y中。连续的1片段在N到y的过程中参与了进位,而其他无关位置的10是不会改变的,所以我们的目标是通过位运算让进位的部分保留,其他位置去掉,先 ~y取反再&N可以让改变的东西变回去,没变的东西就不要了,(读者可以自己举例子验证一下)。
当然上一步的最终目的还未达成,我们要的是z=11110去掉最后10之后剩下的部分,所以还要(z/x)>>1
最后将y和连续1片段拼起来 y |( (z/x)>>1 )

感受:选自寒假周训里的一题,学到许多数学物理公式的灵活运用,继续加油


//
#include <bits/stdc++.h>
using namespace std;

typedef long long ll;


int main()
{
    int T;
    cin>>T;
    int TT=T;
    while(T--)
    {
        int n,x,y,z;
        int res,ans;
        cin>>n;
        x=n&(-n);//lowbit取最后的10串
        y=n+x;//将最后一个连续的1串进位改为10串,如1111->10000
        z=n&(~y);//第一个连续1串,原来变化的部分不变,不变的变=.=
        res=(z/x)>>1;//最右边的10串去掉,要右移一位,去掉进位的1
        ans=y|res;//连续1段和前部分合并
        printf("Case %d: %d\n",TT-T,ans);
    }
}

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页