深度優先搜索(DFS解決枚舉所有子序列的問題)


  用一個例子,理解其中包含的DFS思想。

 

  有n件物品,每件物品的重量是w[i],價值是c[i]。現在需要選出若干件物品放入一個容量為V的背包中,

使得在選入背包的物品重量和不超過容量V的前提下,讓背包中物品的價值之和最大,求最大值n在1到20之間。

 

  在這個問題中,需要從n件物品中選擇若干件物品放入背包中,使它們的價值之和最大。這樣的話,每件物品都有選

或者不選兩種選擇,而這就是迷宮中的“岔道口”。那么什么是“死胡同”呢?——題目要求選擇的物品重量之和不能超過V,

因此一旦選擇的物品重量和超過V,就會達到“死胡同”,需要返回最近的“岔道口”。

  顯然,每次都需要對物品進行選擇,因此DFS函數的參數中必須記錄當前處理的物品編號index。而題目涉及了物品的重量

與價值,因此也需要參數來記錄在處理當前物品之前,已選物品的總重量sumW與總價值sumC。於是DFS的函數大致是這樣的……

1 void DFS(int index,int sumW,int sumC){ 2     //終止條件(死胡同) 3 
4 
5     //岔道口,每次的選擇.
6 }

  於是,如果選擇不放入index物品,那么sumW與sumC就將不變,接下來處理index+1號物品,即前往DFS(index+1,sumW,sumC)這條

分支;而如果選擇放入index號物品,那么sumW將增加當前物品重量和當前物品價值,接着處理index+1號物品,即前往

DFS(index+1,sumW+w[index],sumC+c[index])這條分支。

  一旦index增長到了n,則說明已經把n件物品都處理完了。根據題目要求更新記錄的最大價值。

我們簡單畫一個數組圖。

 

 

 

 

代碼實現如下:

 

 1 #include <stdio.h>
 2 
 3 
 4 #define maxn 30
 5 
 6 int maxPrice = 0;//全局變量,最大價值
 7 int n,V;  8 int w[maxn],c[maxn];  9 
10 void DFS(int index,int sumW,int sumC); 11 
12 int main(){ 13     
14     
15     scanf("%d%d",&n,&V);//提供n件物品和一個容量為Vkg的背包
16     
17     for(int i=0;i<n;i++){ 18         scanf("%d",w+i);//輸入每件物品的重量 
19  } 20     
21     for(int i=0;i<n;i++){ 22         scanf("%d",c+i);//輸入每件物品的價值 
23  } 24     
25     DFS(0,0,0); 26     printf("%d\n",maxPrice); 27     
28     
29     return 0; 30 } 31 
32 
33 void DFS(int index,int sumW,int sumC){ 34     if(index == n){//已經完成對n件物品的選擇(死胡同)
35         if(sumW <= V&&sumC > maxPrice){ 36             maxPrice = sumC; 37  } 38         
39         return ; 40  } 41     
42     //岔道口 
43     DFS(index+1,sumW,sumC);    //不選擇第index件物品
44     DFS(index+1,sumW+w[index],sumC+c[index]);    //選擇第index件物品 
45 }

 

 

  可以注意到,由於每件物品都有兩種選擇,因此上面代碼的復雜度為O(2n),這看起來不是很優秀。但是可以通過算法進行優化,

使其表現出更好的效率。還記得我們之前說的“死胡同”嗎?上面的代碼把index == n作為一個“死胡同”,使得計算量很大,其實

在這之前我們已經遇到“死胡同”了,因為在計算過程中必定存在sumW已經超過題目限定的V這種可能。所以我們可以在“岔道口”判斷

下一步是否是“死胡同”。

 1 void DFS(int index,int sumW,int sumC){  2     if(index == n){//已經完成對n件物品的選擇(死胡同)
 3         return ;  4  }  5     
 6     //岔道口 
 7     DFS(index+1,sumW,sumC);    //不選擇第index件物品  8     //只有加入index件物品后未超過容量V,才能繼續探索
 9     if(sumW + w[index] <= V){ 10         //注意更新最大價值
11         if(sumC + c[index] > maxPrice){ 12             maxPrice = sumC + c[index]; 13  } 14         DFS(index+1,sumW+w[index],sumC+c[index]);    //選擇第index件物品 
15  } 16     
17 }

  可以看到,原先第二條岔路是直接進入的,但是這里先判斷加入第index件物品后是否滿足容量不超過V的要求(“死胡同”),

只有當條件滿足時,才更新最大價值以及進入這條岔路。這樣可以降低計算量,使算法在數據不極端時有很好的表現。這種通過

題目條件的限制來節省DFS計算量的方法稱作剪枝

  事實上,上面的問題給出了一類常見的DFS問題的解決辦法,即給定一個序列,枚舉這個序列的所有子序列(可以不連續)。例如

對於序列{1,2,3}來說,它的子序列如下

{1},{2},{3}

{1,2},{1,3},{2,3}

{1,2,3}

這一類問題通常等價於枚舉從N個整數中算則K個數的所有方案

 舉一個題目例子,可以參照https://www.cnblogs.com/ManOK/p/12552540.html

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM