深度優先搜尋dfs之1到n的全排列(收藏)
阿新 • • 發佈:2018-12-24
- /********
- *給你一個數n,輸出1到n的全排列
- *深度優先搜尋
- ********/
- #include <stdio.h>
- #include <stdlib.h>
- int book[10], a[10], n;
- void dfs(int step)
- {
- int i;
- if(step == n+1)//當你在第n+1步的時候,說明前n部已經排好了。
- {
- for(i = 1; i <= n; i++)
- printf("%d ", a[i]);
- printf("\n");
- return
- }
- for(i = 1; i <= n; i++)//按照1,2,3.。。的方式一一嘗試。
- {
- if(book[i]==0)//判斷撲克牌i是不是還在手裡
- {
- a[step]=i;//將i牌放在第step個盒子裡。
- book[i]=1;//表示撲克牌不再第step個盒子裡
- dfs(step+1);//繼續下一步。
- book[i]=0;//將剛才嘗試的撲克收回,才能進行下一步的嘗試。
- }
- }
- return
- }
- int main()
- {
- while(~scanf("%d", &n))
- dfs(1);
- return 0;
- }
-------------------------------------------------------分割線---------------------------------------------------------------
全排列:
全排列是將一組數按一定順序進行排列,如果這組數有n個,那麼全排列數為n!個。
從集合中依次選出每一個元素,作為排列的第一個元素,然後對剩餘的元素進行全排列,如此遞迴處理,從而得到所有元素的全排列。
以對字串abc進行全排列為例,我們可以這麼做:以abc為例
固定a,求後面bc的排列:abc,acb,求好後,a和b交換,得到bac
固定b,求後面ac的排列:bac,bca,求好後,c放到第一位置,得到cba
固定c,求後面ba的排列:cba,cab。
這個思想和回溯法比較吻合。
程式碼可如下編寫所示
- // 回溯法搜尋全排列樹
- #include<stdio.h>
- #define M 20
- int n;
- int a[M];
- int cnt = 0;// 記錄全排列個數
- void swap(int *a, int *b)//交換a,b
- {
- char t;
- t = *a;
- *a = *b;
- *b = t;
- }
- void dfs(int cur)
- {
- int i;
- if(cur == n)// 找到 輸出全排列
- {
- ++cnt;
- for(i=0; i<n; i++)
- printf("%d ", a[i]);
- printf("\n");
- }
- else
- {
- // 將集合中的所有元素分別與第一個交換,這樣就總是在
- // 處理剩下元素的全排列(即用遞迴)
- for(i=cur; i<n; i++)
- {
- swap(&a[cur], &a[i]);
- dfs(cur+1);
- swap(&a[cur], &a[i]);//回溯
- }
- }
- }
- int main()
- {
- while(scanf("%d", &n) != EOF)
- {
- for(int i=0; i<n; i++)
- a[i] = i+1;// 假設集合S為:1 2 3 ... n
- cnt = 0;
- dfs(0);
- printf("count:%d\n", cnt);
- }
- return 0;
- }
或者利用一個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中。
程式碼如下:
- #include<stdio.h>
- #define M 20
- int n;
- int a[M];
- int vis[M];
- int cnt = 0;// 記錄子集個數
- void dfs(int cur)
- {
- int i;
- if(cur == n)// 找到 輸出所有子集
- {
- ++cnt;
- int flag =1;
- for(i=0; i<n; i++)
- if(vis[i])
- {
- printf("%d ", a[i]);
- flag = 0;
- }
- if(flag)//子集中的空集
- printf("φ");
- printf("\n");
- }
- else
- {
- for(i=1; i>=0; --i)//vis 中分為 選or不選即 1,0
- {
- vis[cur] = i;
- dfs(cur+1);
- }
- }
- }
- int main()
- {
- while(scanf("%d", &n) != EOF)
- {
- for(int i=0; i<n; i++)
- a[i] = i+1;// 假設