遞迴(dfs深度優先搜尋)
阿新 • • 發佈:2021-02-17
遞迴(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
。
這樣就完成了深搜的操作,感覺重要的還是把原過程搞清楚,不然寫遞推會一塌糊塗,不要忘記操作就行。