生成子集 (增量构造法)

使用增量构造法可以构造出升序数组arr的不重复子集,并且按字典序排序

#include<bits/stdc++.h>
using namespace std;
int arr[16];
inline void print_subset(int *index, int cur, int n)///cur值这里可以理解为在这个堆栈层子集的集合数
{
    for(int i=0; i<cur; i++) {printf("%d ", arr[index[i]]);} if(cur)puts("");
    int s = cur ? index[cur-1]+1 : 0;///因为index是arr升序序列的下标,这里cur就是当前arr可能最小值的下标
    for(int i=s; i<n; i++){
        index[cur] = i;
        print_subset(index, cur+1, n);
    }
}
int main(void)
{
    int n, index[16];///index数组辅助构造,其值为升序序列的下标,注意是从0开始
    for(int i=0; i<16; i++) index[i] = i;
    while(~scanf("%d", &n)){
        int cur = 0;
        for(int i=0; i<n; i++) arr[i] = i+1;///这里arr的值为1~n的一个序列
        print_subset(index, cur, n);
        puts("");
    }
    return 0;
}

如果要构造如下这样的排序的话,以输入3为例

1
2
3
1 2
1 3
2 3
1 2 3

可以在原有的基础上使用一个结构体将每个子集的长度和具体序列用int和string存起来就能通过二级排序构造出来了

#include<bits/stdc++.h>
using namespace std;
int arr[16];
struct item
{
    int len, digit[16];
    string s;
};
bool cmp(const item fir, const item sec)
{
    if(fir.len==sec.len) return fir.s < sec.s;
    return fir.len < sec.len;
}
item ans[1<<16];
int top = 0;
inline void print_subset(int *index, int cur, int n)
{
    //for(int i=0; i<cur; i++) {printf("%d ", cur);printf("%d ", arr[index[i]]);} puts("");
    ans[top].len = cur;
    stringstream temp;
    for(int i=0; i<cur; i++){
        ans[top].digit[i] = arr[index[i]];
    }
    temp<<ans[top].digit;
    temp>>ans[top].s;
    top++;
    temp.clear();
    int s = cur ? index[cur-1]+1 : 0;
    for(int i=s; i<n; i++){
        index[cur] = i;
        print_subset(index, cur+1, n);
    }
}
int main(void)
{
    int n, index[16];
    for(int i=0; i<16; i++) index[i] = i;
    while(~scanf("%d", &n)){
        int cur = 0; top = 0;
        for(int i=0; i<n; i++) arr[i] = i+1;
        print_subset(index, cur, n);
        sort(ans, ans+top, cmp);
        for(int i=0; i<top; i++){
//            if(ans[i].len) printf("%d ", ans[i].len);
            for(int j=0; j<ans[i].len-1; j++){
                printf("%d ", ans[i].digit[j]);
            }
            printf("%d", ans[i].digit[ans[i].len-1]);
            puts("");
        }
        puts("");
    }
    return 0;
}

时间: 2024-10-21 10:06:35

生成子集 (增量构造法)的相关文章

【算法竞赛入门经典】7.3子集生成【增量构造法】【位向量法】【二进制法】

7.3.1增量构造法 思路:一次选出一个元素放到集合中.自己对于递归的理解还是不够,这里虽然没有明确给出递归停止条件,但是如果无法继续添加元素,就不会再继续递归,然后就是我头疼的回溯啦. #include<stdio.h> int num[4],n; void A(int n,int *a,int ans) { for(int i = 0; i < ans; i ++)//打印当前元素 printf("%d ",a[i]); printf("\n"

子集生成——增量构造法+位向量法+二进制法

1.增量构造法: 原理图: 1 // 此算法仅用于输出下标,实际运用应输入另一个数组来进行数据的储存 2 #include <bits/stdc++.h> 3 using namespace std; 4 typedef long long ll; 5 typedef unsigned long long ull; 6 #define INF 0X3f3f3f3f 7 const ll MAXN = 1e3 + 7; 8 const ll MOD = 1e9 + 7; 9 int a[10];

子集生成——增量构造法

#include<iostream> using namespace std; int store[100]; int n; void subset(int cur,int s,int cnt){ //cnt表示子集元素的个数 if(cur==cnt){ for(int i=0;i<cur;i++){ cout<<store[i]<<" "; } cout<<endl; } else{ for(int i=s;i<=n;i++

增量构造法 (白书P188)

#include<iostream> #include<cstdio> using namespace std; int ans[6]; int n; void dfs(int cnt) { int i,j; for(i=0;i<cnt;i++) cout<<ans[i]<<" "; if(cnt) cout<<endl; int s=cnt?ans[cnt-1]+1:0; for(i=s;i<n;i++) { a

生成子集

1 生成子集 1.1 含义 给定一个集合,枚举它所有可能的子集. 比如给定集合{1,2,3},应该输出: {} {1} {2} {1, 2} {3} {1, 3} {2, 3} {1, 2, 3} 1.2 增量构造法 增量构造法,每次选择一个元素放到集合中,每次操作的结果即是一个子集. 递归操作,每次向当前集合中添加一个比当前集合中最大的元素大1的数. 代码: from __future__ import print_function def print_subset(n, lst, cur):

利用子集构造法实现NFA到DFA的转换

概述 NFA非有穷自动机,即当前状态识别某个转换条件后到达的后继状态不唯一,这种自动机不便机械实现,而DFA是确定有限状态的自动机,它的状态转换的条件是确定的,且状态数目往往少于NFA,所以DFA能够比较方便的机械实现且识别能力方面也和NFA相当.本次实验采用子集构造法来实现不带空弧的由NFA到DFA的转换. 子集构造法的算法如下: 设NFA为M=(K,Σ,f,S0,Z),则构造相应的DFA  M′=(Q,Σ,f′,I0,F)①取I0=S0:②对于状态集Q中任一尚未标记的状态qi={Si1,Si

(算法初步学习)蛮力法解决生成子集问题

关于蛮力法求生成子集问题 对于算法分析中的问题我已经不想对自己说什么了,到了大三了,竟然还是小白.对生成子集问题我研究了一下午,最后终于想通了.思路:   1.利用<math.h> 头文件中的pow(x,y)函数来求得2的n次方,作为外循环.2.然后写一个将10进制数转换为2进制数的函数(当然,C中有专门的转换进制的函数在<stdlib.h>中,但是,他转换下来  5不是0101而是101,这就导致需要分开判断,所以不用itoa函数).如下:conversation(int n,i

构造增量法生成子集

题意: 生成 1~n 集合的子集, 先按元素从小到大再按字典序排列输出 分析: 所谓构造增量法, 就是每次都输出当前数组的元素, 然后再给当前数组最大元素一个增量, 看是否仍然在集合内, 如果在就把他继续放进数组, 输出. 这种方法不需要显式确认递归边界, 如果无法添加元素, 自然就不会再递归了. 数据结构我选用了string , 因为字典序比较容易排序出来. #include <bits/stdc++.h> using namespace std; string ans[1 <<

增值构造法子集生成

#include<cstdio> void print_subset(int n,int * A,int cur) { for(int i=0;i<cur;i++) printf("%d ",A[i]); printf("\n"); int s = cur ? A[cur-1]+1 : 0; for(int i=s;i<n;i++){ A[cur]=i; print_subset(n,A,cur+1); } } int main() { in