DFS深度優先搜尋-入門 筆記
DFS 依靠遞迴的思想,總是往更遠的方向行進,直到達到邊界,再返回到上一步考慮另外的方向
(遞迴-回溯)
-
遞迴實現指數型列舉
從 \(1\)~\(n\) 這 \(n\) 個整數中隨機選取任意多個,輸出所有可能的選擇方案,即計算\(2^n\)
我們考慮有 \(n\) 個空位,每次都選擇填或不填數字進去
同時記錄該數字的狀態:選擇中、填、不填 一共三種狀態,所以需要一個陣列來儲存他們
int n;
int state[20];
然後來構造 dfs
函式:使用 x
這個變數來作為形式引數,意思是當前考慮到了第幾個空位
void dfs(int x){
......
}
此時第 x
位的狀態為初始化的 \(0\) ,表示在選擇中,每個空位都有兩種選擇:填或不填
我們把填的狀態記作 \(1\) ,不填的狀態記作 \(2\)
state[x]=1;//選擇填入
dfs(x+1);
state[x]=0;
state[x]=2;//選擇不填入
dfs(x+1);
state[x]=0;
每次選擇填或不填後,再次呼叫 dfs
遞迴考慮下一個空位,遞迴完成後,我們將這一位的狀態復位,即state[x]=0
(回溯)
當遞迴到沒有空位的情況時,我們就輸出方案:
if (x>n){//第x位超過了我們給定的n
for (int i=1;i<=n;i++){
if (state[i]==1)//當第i位的狀態為1(選了),我們就輸出該位填入的數
printf("%d ",i);
}
printf("\n");
return ;//終止遞迴,開啟回溯
}
完整程式碼:
#include <stdio.h>
int n;
int state[20];
void dfs(int x){
if (x>n){
for (int i=1;i<=n;i++){
if (state[i]==1)
printf("%d ",i);
}
printf("\n");
return ;
}
state[x]=1;
dfs(x+1);
state[x]=0;
state[x]=2;
dfs(x+1);
state[x]=0;
}
int main()
{
scanf("%d",&n);
dfs(1);
return 0;
}
-
遞迴實現組合型列舉
從 \(1\)~\(n\) 這 \(n\) 個整數中隨機選出 \(m\) 個,輸出所有可能的選擇方案,即計算\(C_n^m\)
DFS要求我們的方案做到不重不漏,對應組合數的計算,採用“不回頭”的策略
還是考慮第 \(x\) 位填不填數字進去,只是需要增加一個引數來記錄我們從第幾個數開始選數字(不回頭)
填第 x
個空位時,我們從第 str
項開始
void dfs(int x, int str){
......
}
使用for
迴圈來對 n
個數依次進行考慮,定義arr
陣列,儲存我們的方案
for (int i=str;i<=n;i++){
arr[x]=i;
dfs(x+1,i+1);
arr[x]=0;
}
從 \(1\) 開始考慮,每次填入 arr
陣列的第 x
位,然後遞迴到第 x+1
位,並從第 i+1
項開始選擇
最後遞迴到 x>n
時結束遞迴,輸出arr
陣列中每一位填入的數
if (x>m){
for (int i=1;i<=m;i++){
printf("%d ",arr[i]);
}
printf("\n");
return;
}
完整程式碼:
#include <stdio.h>
int n,m;
int arr[52];
void dfs(int x, int str){
if (x>m){
for (int i=1;i<=m;i++){
printf("%d ",arr[i]);
}
printf("\n");
return;
}
for (int i=str;i<=n;i++){
arr[x]=i;
dfs(x+1,i+1);
arr[x]=0;
}
}
int main()
{
scanf("%d %d",&n,&m);
dfs(1,1);
return 0;
}
-
遞迴實現排列型列舉
把 \(1\)~\(n\) 這 \(n\) 個整數排成一行後隨機打亂順序,輸出所有可能的次序,即計算\(A_n^n\)
我們一樣考慮第 \(x\) 位填入數字的操作,但是每次填入的數字都可以取未填過的,因為需要考慮排列順序
void dfs(int x){
......
}
我們開一個bool
陣列來記錄數字的狀態(選過了,未選過)
使用for
迴圈來遍歷所有數字的狀態,選擇未被使用過的數字填入第 x
位
for (int i=1;i<=n;i++){
if (!st[i]){//選擇未被使用過的數
arr[x]=i;
st[i]=1;//標記該數已被使用
dfs(x+1);
st[i]=0;//回溯
}
}
當 x>n
即填完了所有空位後,結束遞迴,輸出arr
陣列記錄的填入的數字
if (x>n){
for (int i=1;i<=n;i++){
printf("%d ",arr[i]);
}
printf("\n");
return;
}
完整程式碼:
#include <stdio.h>
int n, arr[10];
bool st[10];
void dfs(int x){
if (x>n){
for (int i=1;i<=n;i++){
printf("%d ",arr[i]);
}
printf("\n");
return;
}
for (int i=1;i<=n;i++){
if (!st[i]){
arr[x]=i;
st[i]=1;
dfs(x+1);
st[i]=0;
}
}
}
int main()
{
scanf("%d",&n);
dfs(1);
return 0;
}