跳转至

枚举子集

枚举定长子集

增量构造法

原问题:\(1,2,3...n\)\(n\) 个数中选 \(m\)

如果选择 1 ,则原问题变为:\(2,3,...n\)\(n-1\) 个数中选 \(m-1\)

所以原问题可以递归求解,代码如下:

void subset(int a[],int l,const int & r,const int & int n,int m){
    // l,r 为枚举区间
    if(m==0){
        // Finished!
        return;
    }
    for(int i=l;i<=r;i++){
        // Select a[i]
        subset(a,l+1,r,n,m-1);
    }
}

注意维护每次调用的入口环境:如果要维护所选择元素的 sum ,应该这样写

void subset(int a[],int l,const int & r,const int & int n,int m,int sum){
    if(m==0){
        cout<<sum<<' ';
        return;
    }
    for(int i=l;i<=r;i++)
        // sum+=a[i]; 入口环境破坏
        subset(a,l+1,r,n,m-1,sum+a[i]);
}

可以看到,递归方法传入参数很多,效率不高

位向量法

用 0 表示 “不选”,1 表示 “选”,则可以将原问题转化为 0-1 序列的全排列问题

手动构造初始 0-1 排列( 最大 排列 )后,再调用 STL 里的 prev_permutation 即可

如:从 6 个元素中选出 3 个,则最大的 0-1 序列为

\[1\ \ 1\ \ 1\ \ 0\ \ 0\ \ 0\]

可用 prev_permutation 函数,详见 枚举排列

代码如下:

// Input:  n, m, a[n];
bool* flag=new bool[n](); // 初始化为 0
for(int i=0;i<m;i++)
    flag[i]=true; // 构造初始排列
while(prev_permutation(flag,flag+n)){
    // do something
}

枚举不定长子集

增量构造法

void subset(int d[],int a[],int l,int r){
    for(int i=0;i<l;i++)
        cout<<d[a[i]];
    if(l) cout<<endl;
    // 放入第 l 个元素
    for(int i=l?a[l-1]+1:0;i<=r;i++){
        // l?a[l-1]+1:0 确定从哪里开始放
        // 以 d[]={1,2,3},a[]={0,1} 为例:
        // 放第三个元素时,要从下标为 a[2-1]+1=2 的开始放 
        a[l]=i;
        subset(d,a,l+1,r);
    }
}

解答树结点 \(2^n\) ,刚好是所有可能的组合数,不能再少,但递归算法天生较慢,可能爆栈

位向量法

1

void subset(int d[],bool a[],int l,int r){
    // [l,r] 枚举区间  e.g. a[10] => [0,9]
    if(l>r){
        for(int i=0;i<=r;i++)
            if(a[i]) cout<<d[i]<<' ';
        cout<<endl;
        return;
    }
    a[l]=0;
    subset(d,a,l+1,r);
    a[l]=1;
    subset(d,a,l+1,r);
}

位向量法易懂、好写,但略慢( 解答树结点 \(2^{n+1}-1\) 略多 )

二进制法

void subset(int d[],int siz){
    // 枚举所有子集对应的二进制编码 0,1,2,...,1<<siz
    for(int i=0;i<(1<<siz);i++){
        print_subset(d,siz,i);
    }
}
void print_subset(int d[],int siz,int code){
    // 打印编码为 code 的子集
    for(int i=0;i<siz;i++){
        if(code&(1<<i)) cout<<d[i]<<' ';
        // 第 i 位为 1 ,输出 d[i]
    }
    cout<<endl;
}

评论