算法期末備考-第3練-回溯法(加強版)


算法期末備考-第3練-回溯法(加強版)

  這次練習主要是復習回溯法,之前一練主要還是學習了子集樹與排序樹的基本操作。

 

主要內容

  回顧知識:數字全排列(子集樹、排序樹)

  回溯法之加強版:素數環

  練習題:數字排序問題(藍橋杯) + 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 }
全排列

 

 


 

素數環

【題目描述】

  素數環是一個計算機程序問題,指的是將從1到n這n個整數圍成一個圓環,若其中任意2個相鄰的數字相加,結果均為素數,那么這個環就成為素數環。現在要求輸入一個n,求n個數圍成一圈有多少種素數環,規定第一個數字是1。

【小結】

  大家千萬不要去新的服務器交題目。我都快自閉了。然后去了以前的服務器上交題過了。但是那個題目的數據有錯,應該需要特判1,因為1本身不是素數。而那道題目的數據雖然涉及n=1的情況,但是答案卻是1,答案應該是“-1”。

【題解】

  全排列的一個變種問題,其實就是在過程中加一個判斷即可。在放數字判斷前一個數與當前位置需要放的數之和是否為質數。因為是一個環,所以最后還需要加一個判斷,即最后一個數與1相加是否為質數。

  總結:朴素全排列問題+過程中特判相鄰數之和為質數+最后一個數與1相加為質數。

 

 1 //dfs解決素數環  hdu-1016
 2 // http://acm.hdu.edu.cn/showproblem.php?pid=1016
 3 #include<cstdio>
 4 #include<cstring>
 5 using namespace std;
 6 const int N = 1e3 +10 ;
 7  8 int n ;
 9 int a[N] ;
10 int vis[N] ;
11 bool is_prime[N] ;
12 13 void Init(){
14     for( int i = 2 ; i < N ; i++ ) {
15         bool f = true;
16         for (int j = 2; j * j <= i; j++) {
17             if (i % j == 0) {
18                 f = false;
19                 break;
20             }
21         }
22         if (f) is_prime[i] = true;
23 24     }
25     a[1] = 1 ;  vis[1] = -1 ;
26 }
27 28 void dfs_subset( int step ){
29     if( step == n + 1 ){
30         if( is_prime[ a[n] + 1 ] ){
31             for( int i = 1 ; i <= n ; i ++ ){
32                 printf("%d%c",a[i],i==n?'\n':' ');
33                 //printf("%d ",a[i]);
34             }
35             //putchar('\n');
36         }
37         return ;
38     }
39 40     for( int i = 2 ; i <= n ; i++ ) {
41         if (vis[i] == 0 && is_prime[i + a[step - 1]]) {
42             vis[i] = 1;     a[step] = i;
43             dfs_subset(step + 1);
44             vis[i] = 0;
45         }
46     }
47 }
48 int main()
49 {
50     Init() ;
51     int Case = 0 ;
52     while( ~scanf("%d",&n) ){
53         printf("Case %d:\n",++Case);
54         if( n > 20 ){
55             printf("-1\n");
56         }else if( n % 2 == 1 ){
57             printf("-1\n");
58         }else{
59             dfs_subset( 2 );
60         }
61         puts("");
62     }
63     return 0 ;
64 }
素數環

 


 

練習題

 

數字排列問題

【題目描述】

今有7對數字:兩個1,兩個2,兩個3,...兩個7,把它們排成一行。 要求,兩個1間有1個其它數字,兩個2間有2個其它數字,以此類推,兩個7之間有7個其它數字。如下就是一個符合要求的排列: 17126425374635 當然,如果把它倒過來,也是符合要求的。 請你找出另一種符合要求的排列法,並且這個排列法是以74開頭的。 注意:只填寫這個14位的整數,不能填寫任何多余的內容,比如說明注釋等。 744*7***

【題解】

直接全排列公式用上,同時要多增加一個標記數組,每個位置放一個數的同時相隔i+1的位置同時放。

然后就OK了。

 1 //74****4*7*******
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 const int N = 20 ;
 6 int vis[N] , a[N];
 7 void dfs( int step ){
 8     if( step == 14 ){
 9         for( int i = 1 ; i <= 14 ; i++ ){
10             printf("%d%c",a[i],i==14?'\n':' ');
11         }
12         return ;
13     }
14 15     if( a[step] != -1 ) dfs( step + 1 );
16 17     for( int i = 1 ; i <= 6 ; i ++ ){
18         if( a[step] == -1 && vis[i] == 0 && step + i + 1 <= 14 && a[ step + i + 1] == -1 ){
19             vis[i] = 1 ;
20             a[step + i + 1] = a[step] = i ;
21 22             dfs( step + 1 );
23 24             a[step + i + 1] = a[step] = -1 ;
25             vis[i] = 0 ;
26         }
27     }
28 }
29 int main(){
30     for( int i = 1 ; i <= 14 ; i ++ ) a[i] = -1 ;
31     vis[4] = vis[7] = -1 ;
32     a[1] = 7 , a[9] = 7 ;
33     a[2] = 4 , a[7] = 4 ;
34     dfs( 3 ) ;
35     return 0 ;
36 }
數字排列問題

 


 

39級台階

【題目描述】

  小明看完電影《第39級台階》,離開電影院的時候,他數了數視覺的台階數,恰好是39級。 站在台階前,他突然又想起一個問題:如果我每一步只能邁上1個或2個台階,先邁左腳,然后左右交替,最后一步邁右腳,也就是說一共要邁偶數步。那么,上完39級台階,有多少種不同的上法呢? 請利用計算機的優勢,幫助小明尋找答案。


【題解】

  利用遞歸來實現,從第0層往上走,上1級或2級。

  題目都給出來了,先邁左腳,最后邁出右腳,可以說明步數為偶數。


 1 #include<cstdio>
 2 using namespace std;
 3 int n = 39 ;
 4 int ans = 0 ;
 5 void dfs( int step , int k ){
 6     //定義結束條件
 7     if( step == 39 && k % 2 == 0 ){
 8         ans ++ ;
 9         return ;
10     }
11     //結束條件需要多加一條
12     if( step >= 40 ) return ;
13 14     //邁出一步,台階增加1或2
15     dfs( step + 1 , k + 1 );
16     dfs( step + 2 , k + 1 );
17 }
18 //51167078
19 int main()
20 {
21     dfs( 0 , 0 );
22     printf("%d\n",ans);
23     return 0;
24 }
39級台階

 


 

數字排列問題

【題目描述】

將1到20排一列,要求每相鄰兩位數的和是質數,試求排列的種數。

【題解】

用排列樹解決,弱化版的素數環問題。

預處理所有素數出來,用bool數組標識出來。

然后放每一個數的時候加上兩數之和為素數即可。

只不過剪枝效果不太好,等個2~3分鍾才出答案。。

 

 1 //dfs解決數字排列問題
 2  3 #include<cstdio>
 4 #include<cstring>
 5 #include<algorithm>
 6 using namespace std;
 7 const int N = 1e3 +10 ;
 8  9 int n ;
10 int a[N] ;
11 bool is_prime[N] ;
12 13 void Init(){
14     for( int i = 2 ; i < N ; i++ ) {
15         bool f = true;
16         for (int j = 2; j * j <= i; j++) {
17             if (i % j == 0) {
18                 f = false;
19                 break;
20             }
21         }
22         if (f) is_prime[i] = true;
23     }
24 }
25 26 int ans = 0 ;
27 void dfs_Permutation( int S , int E ){
28     if( S == E && is_prime[ a[E] + a[E-1] ]){
29         ans ++ ;
30         return ;
31     }
32     for( int i = S ; i <= E ; i++ ) {
33         if( S == 1 || ( is_prime[ a[i] + a[S-1] ] ) ){
34             swap( a[S] , a[i] );
35             dfs_Permutation( S + 1 , E );
36             swap( a[S] , a[i] );
37         }
38     }
39 }
40 int main()
41 {
42     Init() ;
43     n = 20 ;
44     for( int i = 1 ; i <= n ; i ++ ){
45         a[i] = i ;
46     }
47     dfs_Permutation( 1 , n );
48     printf("%d\n",ans);
49     return 0 ;
50 }
數字排列問題(相鄰之和為素數)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM