取子集

#include
#include
#include
using namespace std;
void print(int n)//二进制观察,从左到右对应低位到高位
{
    for(int i=0;(1<

关键代码

 for(int i=sum;i;i=(i-1)&sum)
附:这段代码源于我在大白上看到的类似代码


对于集合sum。1表示该物品可取,0表示不可取,求出所有的可取状态。

上述代码不重复地取出了所有的子集!


下面给出i=(i-1)&sum这一表达式的证明

1.不重复性

证明:      设j=(i-1)∑( i 是已知的正确的状态)

由于取了&sum说明 j 也是正确的状态。

而  j是由(i-1)和某数按位与得到的,

则    j

得证(i-1)&sum能取出不重复的正确的状态来。


2.完整性

证明:    上面谈到了不重复性的证明,其实就是 i 的单调递减的证明

现在我们只需要证明   j=(i-1)&sum  得到的 j 是第一个比 i 小的状态。

设 状态 i  是  abcdef 10000   ; 

i-1     是        abcdef  01111   ;

那么   (i-1)&sum会得到什么呢

我们将  i-1 分解来看  为  (abcdef 00000  + 000000 01111)&sum =(abcdef 00000&sum) + (000000 01111&sum)

                                                                                                                           = abcdef 00000 + (000000 01111&sum)

                注意到    i&sum=i  ( i 是已知的正确状态)

将 i 也分解来看看             (abcdedf 00000  + 000000 10000) &sum = abcdef 00000 + (000000 10000&sum)

假设   状态   s=abcdef 0****是第一个比 i 小的状态

可得:

①    s>=j

        ②    s

由①②可得    s=j  ;

得证  j  是第一个比  i  小的状态 

得证 完整性


由1,2可以证明该算法的正确性 ,并可算出时间复杂度为( 2^n , n为可选物品数)。而所有的子集也为 2^n 个,因此该算法相当的好啊。




这神奇的取子集方法~~


你可能感兴趣的:(取子集)