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

增量构造法

参考

 

//此算法仅仅是输出下标,实际应用应输入另一个数组来进行存储数据
//这三种方式,为了理解这个花费了最长的时间,太纠结于细节了,刘汝佳写的不是特别清楚
//这三种方式的话,都可以理解为输出0 - n-1的这n个数字的子集
//疑问就是我的数列并不是单纯的0 - n-1, 如果不是这种情况,那该怎么办,
//所以说,可以定义一个数据数组,这三种方式所直接输出的集合就是数据数组的下标集合
//说到这里就不难理解下文的代码了
//A数组是下标数组,每次输出的是下标集合
#include
#include
#include

using namespace std;

void print_subset(int n, int *A, int cur) {//cur是当前A数组的位置
    for(int i = 0; i < cur; ++i) {
        printf("%d ", A[i]);//如果有数据数组, 就写成data[A[i]],此处不再赘述
    }
    printf("\n");


    int s = cur ? A[cur - 1] + 1 : 0;
    //这一句是比较难以理解,尤其是A[cur-1] + 1这里,看了好久才搞明白
    //当cur等于0的时候,这是第一次进入函数,所以选取的集合下标元素为0就可以了
    //cur不等于0的时候,即cur前面还有下标元素,为了得到全部的子集,所以这里不能漏掉,便从最小的那一个选择
    //那为什么A[cur-1]+1就是最小的下标呢,刚刚输出的最后一个下标就是A[cur-1],所以这一个下标+1,即还未选择过的最小下标


    //然后就是从当前最小的还未选择过的下标作为下一个下标集合的首元素开始选取
    //直到n-1的下标位置,每一次选取首下标之后都开始递归
    for(int i = s; i < n; ++i) {
        A[cur] = i;
        print_subset(n, A, cur + 1);
    }
}

int main() {
    int n;
    int A[10];//该数组存放的是数据数组的下标,为了简单,并没有定义数据数组,数据数组在输出的时候才用得上
    while(scanf("%d", &n) == 1) {
        print_subset(n, A, 0);
    }
    return 0;
}

子集生成 增量构造法 位向量法 二进制法_第1张图片
位向量法

 

 

//此算法仅仅是输出下标,实际应用应输入另一个数组来进行存储数据
//这个比较好理解,就相当于枚举,跟二进制有点相似, 这个使用数组表示,二进制是使用数字的二进制位置表示
#include
#include
#include

using namespace std;

void print_subset(int n, int *B, int cur) {
    if(cur == n) {                          //已经枚举完一种状态,输出
        for(int i = 0; i < n; ++i) {
            if(B[i]) {
                printf("%d ", i);
            }
        }
        printf("\n");
        return;
    }

    B[cur] = 1;                             //该元素在下一个将要枚举的集合中
    print_subset(n, B, cur + 1);

    B[cur] = 0;                             //该元素不在下一个将要枚举的集合中
    print_subset(n, B, cur + 1);
}


int main() {
    int n, B[10];
    while(cin >> n) {
        print_subset(n, B, 0);
    }
    return 0;
}


二进制法

 

 

 

//此算法仅仅是输出下标,实际应用应输入另一个数组来进行存储数据
//原理:用数字的二进制位表示状态,二进制从右到左的第几个位置 表示数组元素的下标

#include
#include
#include
#include

using namespace std;

void print_subset(int n, int s) {
    //s代表的是当前二进制数表示的状态
    for(int i = 0; i < n; ++i) {
        if(s & 1 << i) {//1左移几位就代表第i个二进制位为1,其他位为0,与状态进行&运算,如果此状态包含该数字,就输出
            printf("%d ", i);
        }
    }
    printf("\n");
}

int main() {
    int n;
    while(scanf("%d", &n) == 1) {
        //一共n个数字,所以其全集有2^n个二进制1,所对应的十进制数字就是2^n - 1
        //我们要做的就是枚举出来每一种状态,1 右移 n 位,所得的十进制数字就是2^n
        for(int i = 0; i < (1 << n) ; ++i) {        //i表示集合元素的状态,根据该状态打印出当前集合
            print_subset(n, i);
        }
    }
    return 0;
}

 

 

 

 

 

 

 

 

你可能感兴趣的:(算法)