标签:
全排列是将一组数按一定顺序进行排列,如果这组数有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