1. 程式人生 > >深度優先搜尋dfs之1到n的全排列(收藏)

深度優先搜尋dfs之1到n的全排列(收藏)

  1. /******** 
  2. *給你一個數n,輸出1到n的全排列 
  3. *深度優先搜尋 
  4. ********/
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. int book[10], a[10], n;  
  8. void dfs(int step)  
  9. {  
  10.     int i;  
  11.     if(step == n+1)//當你在第n+1步的時候,說明前n部已經排好了。 
  12.     {  
  13.         for(i = 1; i <= n; i++)  
  14.             printf("%d ", a[i]);  
  15.         printf("\n");  
  16.         return
     ;//返回之前的一步; 
  17.     }  
  18.     for(i = 1; i <= n; i++)//按照1,2,3.。。的方式一一嘗試。 
  19.     {  
  20.         if(book[i]==0)//判斷撲克牌i是不是還在手裡 
  21.         {  
  22.             a[step]=i;//將i牌放在第step個盒子裡。 
  23.             book[i]=1;//表示撲克牌不再第step個盒子裡 
  24.             dfs(step+1);//繼續下一步。 
  25.             book[i]=0;//將剛才嘗試的撲克收回,才能進行下一步的嘗試。 
  26.         }  
  27.     }  
  28.     return
     ;//結束搜尋。 
  29. }  
  30. int main()  
  31. {  
  32.     while(~scanf("%d", &n))  
  33.         dfs(1);  
  34.     return 0;  
  35.  }   

-------------------------------------------------------分割線---------------------------------------------------------------

全排列:

全排列是將一組數按一定順序進行排列,如果這組數有n個,那麼全排列數為n!個。

從集合中依次選出每一個元素,作為排列的第一個元素,然後對剩餘的元素進行全排列,如此遞迴處理,從而得到所有元素的全排列。

以對字串abc進行全排列為例,我們可以這麼做:以abc為例
固定a,求後面bc的排列:abc,acb,求好後,a和b交換,得到bac
固定b,求後面ac的排列:bac,bca,求好後,c放到第一位置,得到cba
固定c,求後面ba的排列:cba,cab。

這個思想和回溯法比較吻合。

程式碼可如下編寫所示

  1. // 回溯法搜尋全排列樹
  2. #include<stdio.h>
  3. #define M 20
  4. int n;  
  5. int a[M];  
  6. int cnt = 0;// 記錄全排列個數
  7. void swap(int *a, int *b)//交換a,b
  8. {  
  9.     char t;  
  10.     t = *a;  
  11.     *a = *b;  
  12.     *b = t;  
  13. }  
  14. void dfs(int cur)  
  15. {  
  16.     int i;  
  17.     if(cur == n)// 找到 輸出全排列
  18.     {  
  19.         ++cnt;  
  20.         for(i=0; i<n; i++)  
  21.             printf("%d ", a[i]);  
  22.         printf("\n");  
  23.     }  
  24.     else
  25.     {  
  26.          // 將集合中的所有元素分別與第一個交換,這樣就總是在
  27.         // 處理剩下元素的全排列(即用遞迴)
  28.         for(i=cur; i<n; i++)  
  29.         {  
  30.             swap(&a[cur], &a[i]);  
  31.             dfs(cur+1);  
  32.             swap(&a[cur], &a[i]);//回溯
  33.         }  
  34.     }  
  35. }  
  36. int main()  
  37. {  
  38.     while(scanf("%d", &n) != EOF)  
  39.     {  
  40.         for(int i=0; i<n; i++)  
  41.             a[i] = i+1;// 假設集合S為:1 2 3 ... n
  42.         cnt = 0;  
  43.         dfs(0);  
  44.         printf("count:%d\n", cnt);  
  45.     }  
  46.     return 0;  
  47. }  

或者利用一個vis陣列標識每個元素是否已經被訪問,程式碼如下:

  1. #include <stdio.h>
  2. int a[10];  
  3. bool vis[10];  
  4. int n;//排列個數 n
  5. void dfs(int dep)  //列印所有的全排列,窮舉每一種方案
  6. {  
  7.     if(dep == n)  
  8.     {  
  9.         for(int i = 0; i < n; i++)  
  10.         {  
  11.             printf("%d ",a[i]);  
  12.         }  
  13.         printf("\n");  
  14.         return ;  
  15.     }  
  16.     for(int i = 0; i < n; i++)// 找一個最小的未標記的數字,保證了字典序最小
  17.     {  
  18.         if(!vis[i])  
  19.         {  
  20.             a[dep] = i+1;  
  21.             vis[i] = true;// 找到了就標記掉,繼續下一層
  22.             dfs(dep + 1);  
  23.             vis[i] = false;  
  24.         }  
  25.     }  
  26. }  
  27. int main()  
  28. {  
  29.     while(scanf("%d",&n) != EOF)  
  30.     {  
  31.         dfs(0);  
  32.     }  
  33.     return 0;  
  34. }  


子集構造:

從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. #define M 20
  3. int n;  
  4. int a[M];  
  5. int vis[M];  
  6. int cnt = 0;// 記錄子集個數
  7. void dfs(int cur)  
  8. {  
  9.     int i;  
  10.     if(cur == n)// 找到 輸出所有子集
  11.     {  
  12.         ++cnt;  
  13.         int flag =1;  
  14.         for(i=0; i<n; i++)  
  15.             if(vis[i])  
  16.             {  
  17.                 printf("%d ", a[i]);  
  18.                 flag = 0;  
  19.             }  
  20.             if(flag)//子集中的空集
  21.                 printf("φ");  
  22.         printf("\n");  
  23.     }  
  24.     else
  25.     {  
  26.         for(i=1; i>=0; --i)//vis 中分為 選or不選即 1,0
  27.         {  
  28.             vis[cur] = i;  
  29.             dfs(cur+1);  
  30.         }  
  31.     }  
  32. }  
  33. int main()  
  34. {  
  35.     while(scanf("%d", &n) != EOF)  
  36.     {  
  37.         for(int i=0; i<n; i++)  
  38.             a[i] = i+1;// 假設