DFS入門筆記

才瓯發表於2024-11-23

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;
}

相關文章