这次练习主要是复习回溯法,之前一练主要还是学习了子集树与排序树的基本操作。
主要内容
回顾知识:数字全排列(子集树、排序树)
回溯法之加强版:素数环
练习题:数字排序问题(蓝桥杯) + 39级台阶 + 数字排列(相邻之和为素数)
“温故而知新,可以为师矣“
全排列问题
【问题描述】
给定数字n,请输出1~n的全部排列顺序。
例如,n=3,输出:{1,2,3},{1,3,2},{2,1,3},{2,3,1},{3,1,2},{3,2,1}
【题解】
利用两种回溯法的代表解决该问题。
子集树做法:借助 vis[ ] 来标记其在集合中的元素。
构建搜索树,其每一层相当于排列中的每一个位置。
用参数step控制当前位置
该位置填入什么数字,还需看vis[ ]是否被标记过
若该数字已被集合选中,不做处理,则找另外的数字
若该数字未被选中,则把该位置赋值上该数字。同时进行下一层搜索。
当到达叶子结点时,则进行回溯。
回溯时别忘记把当前位置的数字撤标记。
对于当前位置来说又可以填入另外的数字。
其核心代码:
for( int i = 1 ; i <= n ; i ++ ){ if( vis[i] == 0 ){ vis[i] = 1 ; A[step] = i ; dfs_subset( step + 1 ); vis[i] = 0 ; } }
排列树做法:
由于排列必须时n个数,别忘记要初始化数字,直接给赋上值。(1~n)
然后进行排列树的常规操作。
构建搜索树,其每一层相当于排列中的每一个位置。
用参数<S,E>控制,S表示当前位置,E表示结束位置。
我们所需要的是对下标为S,即当前位置的位置分别与后面的数字交换操作。
其目的就是为达到该位置 把全部数字在该位置轮流打头。
到达叶子结点的条件是,当前位置S已经是结束位置E
核心代码:
for( int i = S ; i <= E ; i++ ){ swap( B[S] , B[i] ); dfs_Permutation( S+1 , E ); swap( B[S] , B[i] ); }
具体代码:
1 //DFS 利用子集树 和 排列树 实现数字全排列 2 #include<cstdio> 3 #include<algorithm> 4 using namespace std; 5 const int N = 20 ; 6 int vis[N],n; 7 8 int A[N] , B[N]; 9 void dfs_subset(int step){ 10 if( step == n ){ 11 for( int i = 0 ; i < n ; i++ ){ 12 printf("%3d",A[i]); 13 } 14 putchar('\n'); 15 } 16 for( int i = 1 ; i <= n ; i ++ ){ 17 if( vis[i] == 0 ){ 18 vis[i] = 1 ; A[step] = i ; 19 dfs_subset( step + 1 ); 20 vis[i] = 0 ; 21 } 22 } 23 } 24 25 void dfs_Permutation( int S , int E ){ 26 if( S == E ){ 27 for( int i = 1 ; i <= n ; i++ ){ 28 printf("%3d",B[i]); 29 } 30 putchar('\n'); 31 return ; 32 } 33 for( int i = S ; i <= E ; i++ ){ 34 swap( B[S] , B[i] ); 35 dfs_Permutation( S+1 , E ); 36 swap( B[S] , B[i] ); 37 } 38 } 39 int main() 40 { 41 puts("Please input N :"); 42 scanf("%d",&n); 43 44 puts(" dfs_subset tree"); 45 dfs_subset(0); 46 47 puts(" dfs_Permutation tree"); 48 for( int i = 1 ; i <= n ; i++ ){ 49 B[i] = i ; 50 } 51 dfs_Permutation(1,n); 52 return 0; 53 }