全排列DFS思路詳解
首先考慮一道奧數題目:
問題一:
□□□ + □□□ = □□□,要將數字1~9分別填入9個□中,使得等式成立。例如173+286 = 459。請輸出所有合理的組合的個數。
我們或許可以列舉每一位上所有的數,然後判斷每一位上的數需要互不相等且滿足等式即可,但是用程式碼寫出來需要宣告9個變數且判斷。
那麼我們把這個問題考慮為一個求這個9個數的全排列問題,即可得到更優雅的解答方式。
問題二:
輸入一個數,輸出1~n的全排列。
例項:現在我們考慮有1、2、3的3張撲克牌和編號為1、2、3的3個盒子,需要將這3張撲克牌放到3個盒子裡,求其所有可能性。
解析:
- 首先我們考慮1號盒子,我們約定每到一個盒子面前都按數字遞增的順序擺放撲克牌。
2.接著考慮2號盒子,現在我們手裡剩下2號和3號撲克牌,於是我們可以把2號撲克牌放入2號盒子中。於是在3號盒子只剩一種可能性,我們繼 續把3號撲克放入3號盒子。此時產生了一種排列——{1,2,3}。
3.接著我們收回3號盒子中的3號撲克牌,嘗試一種新的可能,此時發現別無他選。於是選擇回到2號盒子收回2號撲克。
4.在2號盒子中我們放入3號撲克,於是自然而然的在3號盒子中只能放入2號撲克。此時產生另一種排列——{1,3,2};
5.重複以上步驟就能得到數字{123}的全排列。
1、現在我們用C語言程式碼描述往每個小盒子中放入所有可能撲克牌的步驟:
for(int i = 1; i <= n; i++){ a[step] = i; //將i號撲克牌放入第step個盒子中 }
2、a是一個裝入了所有小盒子的陣列,變數step表示當前正處於第step號小盒子。i則表示撲克牌的序號。現在我們需要考慮另外一個問題,則如果一張撲克牌已經被放入別的盒子中,則不能再被放入當前盒子。
因此需要一個book陣列標記哪些牌已經被使用。此時我們完善上述程式碼。
for(int i = 1; i <= n; i++)
{ if(book[i] == 0){ a[step] = i; //將i號撲克牌放入第step個盒子中 book[i] = 1; // 置1表示第i號撲克牌不在手中 } }
現在對於step號盒子已經處理完,那麼我們要考慮step+1號盒子。第step+1個的盒子的處理方式與第step個盒子的處理方式完全一樣。因此,我們可以對上述操作做一個封裝。
void dfs(int step)
{
//step表示當前要處理的盒子
for(int i = 1; i <= n; i++)
{
if(book[i] == 0)
{ a[step] = i;
//將i號撲克牌放入第step個盒子中
book[i] = 1; // 置1表示第i號撲克牌不在手中
} } }
於是我們重新回想文章開頭闡述的放置撲克牌的思路:
我們在當前盒子放置完第i個撲克牌之後,便立即處理下一個盒子。於是:
void dfs(int step)
{
//step表示當前要處理的盒子
for(int i = 1; i <= n; i++)
{
if(book[i] == 0)
{
a[step] = i; //將i號撲克牌放入第step個盒子中
book[i] = 1; // 置1表示第i號撲克牌不在手中
dfs(step+1); //遞迴呼叫
book[i] = 0;
// 非常重要,收回該盒子中的撲克牌才能進行下一次嘗試。
} } }
需要注意到的是,我們需要收回每一次嘗試的撲克牌i,才能進行下一次嘗試。
現在需要考慮最後一個問題,那就是什麼時候得到一個滿足要求的排列,也就是考慮終止條件。這裡很容易得到,當我們處理完成第n個盒子的時候,就已經得到一個符合要求的排列了。加上終止條件的程式碼如下:
void dfs(int step){
//step表示當前要處理的盒子
if(step == n+1)
{
//輸出排列
for(i = 1; i <= n; i++) printf("%d", a[i]);
printf("\n"); return; }
for(int i = 1; i <= n; i++)
{
if(book[i] == 0)
{ a[step] = i;
//將i號撲克牌放入第step個盒子中
book[i] = 1;
// 置1表示第i號撲克牌不在手中
dfs(step+1); //遞迴呼叫
book[i] = 0; // 非常重要,收回該盒子中的撲克牌才能進行下一次嘗試。 } } }
現在深度優先搜尋(DFS)的基本模型展現在我們眼前。
其核心在於,在當前步驟要把每一種可能性都嘗試一遍(使用for迴圈),解決完當前步驟後進入下一步。而下一步的解決方式完全等同於當前步驟的解決方法。於是可以總結出DFS的基本模型:
void dfs(int step){
*判斷結束邊界* 嘗試每一種可能
for(i = 1; i <= n; i++)
{
嘗試下一步 dfs(step + 1);
} return; }