暴力与回溯

1.生成1~n的排列

#include <stdio.h>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
//使用普通数组实现
void print_permutation(int n, int *A, int cur, int &cnt){ //由于无法知道数组的元素个数,用cur进行保存
    if(cur == n){
        cnt++;
        for(int i = 0; i < n; i++)printf("%d", A[i]);
        printf("\n");
    }else{
        for(int i = 1; i <= n; i++){
            bool flag = true;
            for(int j = 0; j < cur; j++){
                if(A[j] == i){flag = false; break;}     //不能选择里面已经有的数字
            }
            if(flag){
                A[cur] = i;
                print_permutation(n, A, cur+1, cnt);         //递归调用
            }
        }
    }
}

//使用vector容器实现
void print_permutation2(int n, vector<int>A, int &cnt){
    if((int)A.size() == n){
        cnt++;
        for(int i = 0; i < n; i++)printf("%d", A[i]);
        printf("\n");
    }else{
        for(int i = 1; i <= n; i++){
            vector<int>::iterator iter=find(A.begin(),A.end(),i);
            if(iter == A.end()){
                A.push_back(i);
                print_permutation2(n, A, cnt);
                A.erase(A.end() - 1);
            }
        }
    }
}

int main(){
    int n = 6, A[10], cnt = 0;
    vector<int>B;
    printf("使用普通数组实现\n\n\n");
    print_permutation(n, A, 0, cnt);
    printf("\n");
    printf("使用vector实现\n\n");
    cnt  = 0;
    print_permutation2(n, B, cnt);
    printf("共计%d个\n", cnt);
}
  1. 生成可以重复的的排列
void print_permutationR(int n, int *A, int cur, int &cnt, int *P){
    if(cur == n){
        cnt++;
        for(int i = 0; i < n; i++)printf("%d", A[i]);
        printf("\n");
    }else{
        for(int i = 0; i < n; i++){
            int c1 = 0, c2 = 0;
            for(int j = 0; j < cur; j++)if(A[j] == P[i]) c1++;
            for(int j = 0; j < n; j++)if(P[i] == P[j]) if(!i || P[i] != P[i-1]) c2++;//不重复不遗漏取遍P的值
            if(c1 < c2){
                A[cur] = P[i];
                print_permutationR(n, A, cur+1, cnt, P);
            }
        }
    }
}

3.子集生成
方法一:增量构造法

void print_subset3(int n, int *A, int cur, int num[]){
    printf("[ ");
    for(int i = 0; i < cur; i++){
        printf("%d ", num[A[i]]);
    }
    printf("]\n");
    int s = cur ? A[cur-1]+1 : 0;
    for(int i = s; i < n; i++){
        A[cur] = i;
        print_subset3(n, A, cur+1, num);
    }
}
int main(){
    int n = 3, A[10], P[3] = {1, 2, 3};
    vector<int>B;
    printf("使用增量构造法:\n");
    print_subset3(n, A, 0, P);
}

方法二:使用位向量法

void print_subset2(int n, int *B, int cur, int num[]){
    if(cur == n){
        printf("[ ");
        for(int i = 0; i < cur; i++){
            if(B[i])printf("%d ", num[i]);
        }
        printf("]\n");
        return ;
    }
    B[cur] = 1;                             //选择cur;
    print_subset2(n, B, cur+1, num);
    B[cur] = 0;                             //不选cur;
    print_subset2(n, B, cur+1, num);

}
int main(){
    int n = 3, A[10], P[3] = {1, 2, 3};
    printf("使用位向量法:\n");
    print_subset2(n, A, 0, P);
}

方法三,使用二进制方法
1.描述子集:使用二进制的一位表示子集的某个元素是否存在。
这样全集就可以使用(1<<n) - 1来表示,子集就是[0, 1<<n - 1]的一个数。

2.输出集合:使用了二进制的与,如果(s & (1<<i))表示子集的第i位存在,则输出。


void print_subset(int n, int s, int num[]){
    printf("[ ");
    for(int i = 0; i < n; i++){
        if(s&(1<<i))printf("%d ", num[i]);
    }
    printf("]\n");
}
void get_subset(int n, int num[]){
    for(int i = 0; i < (1<<n); i++){
        print_subset(n, i, num);
    }
}
int main(){
	int P[3] = {1, 1, 3};
    printf("子集为:\n");
    get_subset(3, P);
}

1.回溯实例

猜你喜欢

转载自blog.csdn.net/fuzekun/article/details/113443026
今日推荐