二进制枚举--最通俗易懂的讲解

二进制:是计算技术中广泛采用的一种数制。二进制数据是用0和1两个数码来表示的数。它的基数为2,进位规则是“逢二进一”,借位规则是“借一当二”

子集:是一个数学概念:如果集合A任意一个元素都是集合B的元素,那么集合A称为集合B子集

含有N个元素的集合的一切子集的个数为 2^n。简单证明一下:

  • 含有0个元素的子集有C(N,0)个,
  • 含有1个元素的子集有C(N,1)个,
  • 含有2个元素的子集有C(N,2)个,
  • .........
  • 含有N个元素的子集有C(N,N)

由二项式系数的性质可得:C(N,0)+C(N,1)+C(N,2)+...+C(N,N)=2^n

我们就是利用了二进制的特性 ,比如说有5个木棍的长度:4, 8, 2, 6, 7。我们就可以用01代表不选

如果选出长度为4、2、6的木棍,则如下表:

                      4                    8                  2            6             7
二进制                     1                     0                  1            1             0
木棍的状态                    选                  不选                 选           选           不选

上面说有5个木棍如果全选的话就是11111对应的十进制数就是31,[0,2^{n}-1] 这个区间上每一个整数代表一个集合一共 2^n 个集合,上表就是数字22(二进制:10110)所代表的集合:4、2、6。

所以我们遍历每一个集合:

for(int i = 0; i < (1 << n); i++)

设s = 13(二进制为1101)代表我们选0 2 3位置上的数值;

那么我们如何找到每个位置上的数值呢?

我们遍历的是二进制的十进制表示(比如13),我们当然可以转化为二进制再枚举每一位,但是,这很麻烦;

一个很巧妙的方式就是利用位运算。

1<<0=1(0);

1<<1=2(10);

1<<2=4(100);

1<<3=8(1000);

1<<4=16(10000);

...

1<<7=128(10000000);

...

看出来了吧!我们只需要将13&(1<

补充一波位运算的知识吧:

按位与运算符(&)

参加运算的两个数据,按二进制位进行“与”运算。

运算规则:0&0=0;  0&1=0;   1&0=0;    1&1=1;

      即:两位同时为“1”,结果才为“1”,否则为0

例如:3&5  即 0000 0011& 0000 0101 = 00000001  因此,3&5的值得1。

左移运算(<<)

 a << b就表示把a转为二进制后左移b位(在后面添b个0)。例如100的二进制为1100100,而110010000转成十进制是400,那么100 << 2 = 400。可以看出,a << b的值实际上就是a乘以2的b次方,因为在二进制数后添一个0就相当于该数乘以2(这样做要求保证高位的1不被移出)。
通常认为a << 1比a * 2更快,因为前者是更底层一些的操作。因此程序中乘以2的操作请尽量用左移一位来代替。

因此,我们便有了:

for(int j = 0; j < n; j++)
        if(i & (1 << j))
            printf(" %d ",a[j]);

那么完整的代码就是: 

#include 
using namespace std;
int main()
{
    int n;
    cin >> n;
    for(int i = 0; i < (1<

例题1:ALGO-115_蓝桥杯_算法训练_和为T点击这里

例题2:HDU5616--Jam's balance点击这里

参考:

https://www.cnblogs.com/SunQi-lvbu/p/7305779.html

https://blog.csdn.net/riba2534/article/details/79834558

你可能感兴趣的:(【算法知识】)