1. 程式人生 > 其它 >遞迴(dfs深度優先搜尋)

遞迴(dfs深度優先搜尋)

技術標籤:ACM演算法

遞迴(dfs深度優先搜尋)

遞迴演算法一般用於問題可抽象為一下公式的情況:

背景問題:給你一個長度為3的環形陣列,請你往裡面填數字1–20,要求不能重複,而且相鄰兩個數的和為質數。 請輸出所有的可能方案。

這種問題的首先想到的是用for迴圈堆疊,這樣的思路很清晰,但是卻很冗長並且不適宜解決資料更大的問題。

對於長度為3,用3個for迴圈來解決,程式碼如下:

bool vis[20];
int ans = 0;
int a[20];

bool isprime(int x)
{
    for(int i = 2;i <= sqrt(x);i++)
    {
        if
(x % i == 0) { return false; } } return true; } int main() { for(int i = 1;i <= 20;i++) { if(vis[i]) { continue; } vis[i] = true; a[1] = i; for(int j = 1;j <= 20;j++) { if(
vis[j]) { continue; } if(!isprime(a[1] + j)) { continue; } vis[j] = true; a[2] = j; for(int k = 1;k <= 20;k++) { if(vis[k]) { continue
; } if(!isprime(a[2] + k) || !isprime(a[1] + k)) { continue; } ans++; } vis[j] = false; } vis[i] = false; } return 0; }

現在把這些步驟寫為遞迴,通過分析,寫的每一個迴圈都要遵從以下的步驟:

1.排除非法情況
2.記錄相關資訊
3.下一層操作
4.消除vis

這也是深搜模板的套路,先判斷是否達到目標,若達到了目標,判斷當前的狀態是否計入答案,沒達到就列舉可能的狀態,記錄本輪的選擇,進入下一狀態。

下面給出上述問題的遞迴(dfs)操作:

bool vis[20];
int ans = 0;
int a[20];
int n;

bool isprime(int x)
{
    for(int i = 2;i <= sqrt(x);i++)
    {
        if(x % i == 0)
        {
            return false;
        }
    }
    return true;
}

void dfs(int now)
{
    if(now == n + 1)
    {
        if(isprime(a[n] + a[1]))
        {
            ans++;
        }
        return;
    }
    for(int i = 1;i <= 20;i++)
    {
        if(!vis[i])
        {
            if(now > 1 && !isprime(a[now - 1] + i))
            {
                continue;
            }
            vis[i] = true;
            a[now] = i;
            dfs(now + 1);//迴圈n次
            vis[i] = false;
    }
    }
}

可以類比斐波那契數列的操作,因為要進行n次的for迴圈操作,因此搜尋終止的條件就是now == n + 1 ,並且對於每一次的搜尋的for迴圈的操作都是一樣的,先用vis[i]判斷是否用過這個數,並且在now > 1的情況下,上一個符合要求的數在這一迴圈也要符合要求(指和為質數)。然後記錄資訊,繼續向深裡迴圈,最後記得處理乾淨,即vis[i] = false

這樣就完成了深搜的操作,感覺重要的還是把原過程搞清楚,不然寫遞推會一塌糊塗,不要忘記操作就行。