码迷,mamicode.com
首页 > 其他好文 > 详细

回溯法 求集合全排列、子集

时间:2016-04-15 07:03:48      阅读:272      评论:0      收藏:0      [点我收藏+]

标签:

全排列:

全排列是将一组数按一定顺序进行排列,如果这组数有n个,那么全排列数为n!个。

从集合中依次选出每一个元素,作为排列的第一个元素,然后对剩余的元素进行全排列,如此递归处理,从而得到所有元素的全排列。

以对字符串abc进行全排列为例,我们可以这么做:以abc为例
固定a,求后面bc的排列:abc,acb,求好后,a和b交换,得到bac
固定b,求后面ac的排列:bac,bca,求好后,c放到第一位置,得到cba
固定c,求后面ba的排列:cba,cab。

这个思想和回溯法比较吻合。

代码可如下编写所示

 1 // 回溯法搜索全排列树  
 2   
 3 #include<stdio.h>  
 4   
 5 #define M 20  
 6 int n;  
 7 int a[M];  
 8 int cnt = 0;// 记录全排列个数  
 9   
10 void swap(int *a, int *b)//交换a,b  
11 {  
12     char t;  
13     t = *a;  
14     *a = *b;  
15     *b = t;  
16 }  
17   
18 void dfs(int cur)  
19 {  
20     int i;  
21     if(cur == n)// 找到 输出全排列  
22     {  
23         ++cnt;  
24         for(i=0; i<n; i++)  
25             printf("%d ", a[i]);  
26         printf("\n");  
27     }  
28     else  
29     {  
30          // 将集合中的所有元素分别与第一个交换,这样就总是在  
31         // 处理剩下元素的全排列(即用递归)  
32         for(i=cur; i<n; i++)  
33         {  
34             swap(&a[cur], &a[i]);  
35             dfs(cur+1);  
36             swap(&a[cur], &a[i]);//回溯  
37         }  
38     }  
39 }  
40   
41   
42 int main()  
43 {  
44     while(scanf("%d", &n) != EOF)  
45     {  
46         for(int i=0; i<n; i++)  
47             a[i] = i+1;// 假设集合S为:1 2 3 ... n  
48         cnt = 0;  
49         dfs(0);  
50         printf("count:%d\n", cnt);  
51     }  
52     return 0;  
53 }  

或者利用一个vis数组标识每个元素是否已经被访问,代码如下:

 

#include <stdio.h>  
  
int a[10];  
bool vis[10];  
int n;//排列个数 n  
void dfs(int dep)  //打印所有的全排列,穷举每一种方案  
{  
    if(dep == n)  
    {  
        for(int i = 0; i < n; i++)  
        {  
            printf("%d ",a[i]);  
        }  
        printf("\n");  
        return ;  
    }  
    for(int i = 0; i < n; i++)// 找一个最小的未标记的数字,保证了字典序最小  
    {  
        if(!vis[i])  
        {  
            a[dep] = i+1;  
            vis[i] = true;// 找到了就标记掉,继续下一层  
            dfs(dep + 1);  
            vis[i] = false;  
        }  
    }  
}  
int main()  
{  
    while(scanf("%d",&n) != EOF)  
    {  
        dfs(0);  
    }  
    return 0;  
}  

子集构造:

从n个元素的集合S中找出S满足某种性质的子集时,相应解空间树称为子集树。

 

求n个元素集合的子集,如A = {1, 2, 3}则A集合的子集有:  
 P(A) = {{1,2,3}, {1,2}, {1,3},{1},{2,3},{2},{3},{}}

 

采用位向量法,构造一个位向量vis[], vis[i] = 1 表示i在子集A中。

代码如下:

 1 #include<stdio.h>  
 2   
 3 #define M 20  
 4 int n;  
 5 int a[M];  
 6 int vis[M];  
 7 int cnt = 0;// 记录子集个数  
 8   
 9   
10 void dfs(int cur)  
11 {  
12     int i;  
13     if(cur == n)// 找到 输出所有子集  
14     {  
15         ++cnt;  
16         int flag =1;  
17         for(i=0; i<n; i++)  
18             if(vis[i])  
19             {  
20                 printf("%d ", a[i]);  
21                 flag = 0;  
22             }  
23             if(flag)//子集中的空集  
24                 printf("φ");  
25         printf("\n");  
26     }  
27     else  
28     {  
29         for(i=1; i>=0; --i)//vis 中分为 选or不选即 1,0  
30         {  
31             vis[cur] = i;  
32             dfs(cur+1);  
33         }  
34     }  
35 }  
36   
37   
38 int main()  
39 {  
40     while(scanf("%d", &n) != EOF)  
41     {  
42         for(int i=0; i<n; i++)  
43             a[i] = i+1;// 假设集合S为:1 2 3 ... n  
44         cnt = 0;  
45         dfs(0);  
46         printf("count:%d\n", cnt);  
47     }  
48     return 0;  
49 }  

此外可以采用采用增量构造法,
代码如下:

 1 #include<stdio.h>  
 2   
 3 #define M 20  
 4 int n;  
 5 int a[M];  
 6 int vis[M];  
 7 int cnt = 0;// 记录子集个数  
 8   
 9   
10 void subset(int cur, int A[])  
11 {  
12     int i;  
13     ++cnt;  
14     if(0 == cur)//子集中的空集  
15         printf("φ");  
16     for(i=0; i<cur; ++i)// 打印当前集合  
17     {  
18         printf("%d ", A[i]);  
19     }  
20     printf("\n");  
21       
22     int min  = cur ? A[cur-1]:0;//确定当前元素的最小可能值  
23     for(i=min; i<n; ++i)  
24     {  
25         A[cur] = a[i];  
26         subset(cur+1, A);//递归构造  
27     }  
28 }  
29   
30   
31 int main()  
32 {  
33     while(scanf("%d", &n) != EOF)  
34     {  
35         for(int i=0; i<n; i++)  
36             a[i] = i+1;// 假设集合S为:1 2 3 ... n  
37         cnt = 0;  
38         int A[M]={0};  
39         subset(0, A);  
40         printf("count:%d\n", cnt);  
41     }  
42     return 0;  
43 }  

 

回溯法 求集合全排列、子集

标签:

原文地址:http://www.cnblogs.com/jiangchen/p/5393848.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!