本篇摘要
本篇介绍一个非常给力的求组合的算法!上一篇“c_c++刁钻问题各个击破之位运算及其实例(2)”介绍了6个比较复杂的位操作,但是没有给出任何应用实例,本篇就之前谈到的位操作进行应用,其主要内容是用位操作来实现求组合。
引例
先来看一道题目,这个题目是理解利用位操作求组合的关键。它是POJ的2453。英文原题就不贴了,我用中文描述一下吧:给定一个正整数N,求最小的、比N大的正整数M,使得M与N的二进制表示中有相同数目的1。
上面的题目描述或许有点拗口,举个例子把,假如给定的N为78,其二进制表示为1001110,包含4个1,那么最小的比N大的并且二进制表示中只包含4个1的数是83,其二进制是1010011,因此83就是答案。那么如何求解这个问题呢?这里给出两个思路:
(1) 最直观的思路:枚举
思路是从N+1开始枚举,对每个数都测试其二进制表示中的1的个数是否与N的二进制表示中1的个数相等,遇到第一次相等时就停止。其算法流程如下:
1、 求得N的二进制表示中1的个数k;
2、 N++;
3、 测试N的二进制表示中所包含的1的个数是否等于k,如果是,则输出N后结束,否则转2;
上面的过程可以用如下的流程图来表示:
用代码实现上述流程如下:
int GetNextN(unsigned int N)
{
int k = count1Bits(N);
do
{
N++;
}while(count1Bits(N)!=k);
return N;
}
上面的代码中还有一个问题没有解决,那就是“求整数的二进制表示中有多少个1”即count1Bits()函数。这个问题在我之前的《c/c++刁钻问题各个击破之位运算及其实例(1)》中有详细阐述,它应用了n&=(n-1)能将n的二进制表示中的最右边的1翻转为0的事实。只需要不停地执行n&=(n-1),直到n变成0为止,那么翻转的次数就是原来的n的二进制表示中1的个数,其代码如下:
int count1Bits(unsigned int n)
{
int count = 0;
while(n)
{
count++;
n&=(n-1);
}
return count;
}
至此,第一个枚举方法介绍完毕,你只需要写如下的main()函数就能测试本方法:
void main()
{
int N;
cin>>N;
cout<<