动态规划算法(Dynamic Programming)之0-1背包问题
文章目錄
- 1. 問題引入
- 2. 動態(tài)規(guī)劃求解0-1背包
- 3. 復(fù)雜度
- 4. 0-1背包升級版(帶價值)
- 5. 0-1背包升級版(帶價值)DP解法
1. 問題引入
前面講了0-1背包的回溯解決方法,它是窮舉所有可能,復(fù)雜度是指數(shù)級別的,如何降低時間復(fù)雜度呢?
對于一組不同重量、不可分割的物品,我們需要選擇一些裝入背包,在滿足背包最大重量限制的前提下,背包中物品總重量的最大值是多少呢?
假設(shè)背包的最大承載重量是9。有5個不同的物品,重量分別是2,2,4,6,3。把回溯求解過程,用遞歸樹畫出來,就是下面這個樣子:
從上面圖中可以看出有些函數(shù)被重復(fù)計算(之前遞歸中也講到了),我們希望當(dāng)計算到已經(jīng)計算過的函數(shù)時,不要重復(fù)計算,直接拿來用,避免重復(fù)勞動。
還是前面的0-1背包問題,分別添加和不添加重復(fù)計算判斷語句,查看效果
#include <iostream> #define MaxWeight 9 //背包承載極限 using namespace std; bool mem [5][9]; int counttimes; void fill(int i, int curWeight, int *bag, int N, int &maxweightinbag) {cout << "調(diào)用次數(shù): " << ++counttimes << endl;if(curWeight == MaxWeight || i == N)//到達極限了,或者考察完所有物品了{if(curWeight > maxweightinbag)maxweightinbag = curWeight;//記錄歷史最大裝載量return;}//-----注釋掉以下3行查看效果-------if(mem[i][curWeight])return;mem[i][curWeight] = true;//---------------------------------fill(i+1,curWeight,bag,N,maxweightinbag);//不選擇當(dāng)前i物品,cw不更新if(curWeight+bag[i] <= MaxWeight)//選擇當(dāng)前i物品,cw更新{//沒有達到極限,繼續(xù)裝fill(i+1,curWeight+bag[i],bag,N,maxweightinbag);} } int main() {const int N = 5;int bag[N] = {2,2,4,6,3};int maxweightinbag = 0;fill(0,0,bag,N,maxweightinbag);cout << "最大可裝進背包的重量是:" << maxweightinbag;return 0; }
2. 動態(tài)規(guī)劃求解0-1背包
- 把整個求解過程分為n個階段,每個階段會決策一個物品是否放到背包中。每個物品決策(放入或者不放入背包)完之后,背包中的物品的重量會有多種情況,也就是說,背包會達到多種不同的狀態(tài),對應(yīng)到遞歸樹中,就是有很多不同的節(jié)點。
- 把每一層重復(fù)的狀態(tài)(節(jié)點)合并,只記錄不同的狀態(tài),然后基于上一層的狀態(tài)集合,來推導(dǎo)下一層的狀態(tài)集合。我們可以通過合并每一層重復(fù)的狀態(tài),這樣就保證每一層不同狀態(tài)的個數(shù)都不會超過MaxWeight個(MaxWeight表示背包的承載極限)。于是,我們就成功避免了每層狀態(tài)個數(shù)的指數(shù)級增長。
- 用一個二維數(shù)組states[N][MaxWeight+1], 來記錄每層可以達到的不同狀態(tài)
- 第0個(下標(biāo)從0開始編號)物品的重量是2,要么裝,要么不裝,決策完之后,會對應(yīng)背包的兩種狀態(tài),背包中物品的總重量是0或者2。我們用states[0][0]=true和states[0][2]=true 來表示這兩種狀態(tài)。
- 第1個物品的重量也是2,基于之前的背包狀態(tài),在這個物品決策完之后,不同的狀態(tài)有3個,背包中物品總重量分別是0(0+0),2(0+2 or 2+0),4(2+2)。我們用states[1][0]=true,states[1][2]=true,states[1][4]=true來表示這三種狀態(tài)。
- 只需要在最后一層,找一個值為true的最接近 MaxWeight(這里是9)的值,就是背包中物品總重量的最大值。
3. 復(fù)雜度
上面就是一種用動態(tài)規(guī)劃解決問題的思路。把問題分解為多個階段,每個階段對應(yīng)一個決策。記錄每一個階段可達的狀態(tài)集合(去掉重復(fù)的),然后通過當(dāng)前的狀態(tài)集合,來推導(dǎo)下一階段的狀態(tài)集合,動態(tài)地往前推進。
-
用回溯算法解決這個問題的時間復(fù)雜度O(2n),是指數(shù)級的。
-
上面DP代碼耗時最多的部分是代碼中的兩層for循環(huán),所以時間復(fù)雜度是O(N * MaxWeight)。N表示物品個數(shù),MaxWeight表示背包承載極限。
-
盡管動態(tài)規(guī)劃的執(zhí)行效率比較高,但是就上面DP代碼實現(xiàn)來說,我們需要額外申請一個N*(MaxWeight+1)的二維數(shù)組,對空間的消耗比較多。所以,動態(tài)規(guī)劃是一種空間換時間的解決思路。有什么辦法可以降低空間消耗嗎?
-
實際上,我們只需要一個大小為 MaxWeight+1 的一維數(shù)組就可以解決這個問題。動態(tài)規(guī)劃狀態(tài)轉(zhuǎn)移的過程,都可以基于這個一維數(shù)組來操作。具體的代碼如下。
內(nèi)層for循環(huán),j 需要從大到小處理,如果從小到大,會出現(xiàn)重復(fù)計算
4. 0-1背包升級版(帶價值)
每個物品對應(yīng)著一種價值,不超過背包載重極限,求可裝入背包的最大總價值。
//--------回溯解法------------------- int maxV = -1; // 最大價值放到 maxV 中 int weight[5] = {2,2,4,6,3}; // 物品的重量 int value[5] = {3,4,8,9,6}; // 物品的價值 int N = 5; // 物品個數(shù) int MaxWeight = 9; // 背包承受的最大重量 void f(int i, int cw, int cv) { // 調(diào)用 f(0, 0, 0)if (cw == MaxWeight || i == N) { // cw==MaxWeight 表示裝滿了,i==N 表示物品都考察完了if (cv > maxV) maxV = cv;return;}f(i+1, cw, cv); // 選擇不裝第 i 個物品if (cw + weight[i] <= w) {f(i+1, cw+weight[i], cv+value[i]); // 選擇裝第 i 個物品} }對上面代碼畫出遞歸樹,每個節(jié)點表示一個狀態(tài)。現(xiàn)在我們需要3個變量(i,cw,cv)來表示一個狀態(tài)。其中,i表示即將要決策第i個物品是否裝入背包,cw表示當(dāng)前背包中物品的總重量,cv表示當(dāng)前背包中物品的總價值。
- 我們發(fā)現(xiàn),在遞歸樹有幾個節(jié)點的 i 和 cw 是完全相同的,比如(2,2,4)和(2,2,3)。
- 在背包中物品總重量一樣的情況下,f(2,2,4)這種狀態(tài)的物品總價值更大,可以舍棄 f(2,2,3)這種狀態(tài),只需要沿著 f(2,2,4)這條決策路線繼續(xù)往下決策就可以。
- 也就是,對于(i,cw)相同的不同狀態(tài),只需要保留 cv 值最大的那個,繼續(xù)遞歸處理,其他狀態(tài)不予考慮。
5. 0-1背包升級版(帶價值)DP解法
- 把整個求解過程分為n個階段,每個階段會決策一個物品是否放到背包中。
- 每個階段決策完之后,背包中的物品的總重量以及總價值,會有多種情況。
- 用一個二維數(shù)組 states[N][MaxWeight+1],來記錄每層可以達到的不同狀態(tài)。
- 這里數(shù)組存儲的值不再是bool類型的了,而是當(dāng)前狀態(tài)對應(yīng)的最大總價值。
- 把每一層中(i,cw)重復(fù)的狀態(tài)(節(jié)點)合并,只記錄cv值最大的那個狀態(tài),然后基于這些狀態(tài)來推導(dǎo)下一層的狀態(tài)。
時間和空間復(fù)雜度都是O(N * MaxWeight)
使用一維數(shù)組也可DP解題,空間復(fù)雜度減小為O(MaxWeight)
最大可裝進背包的價值是:18
一維數(shù)組變化過程如下:
總結(jié)
以上是生活随笔為你收集整理的动态规划算法(Dynamic Programming)之0-1背包问题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql建表_128、mysql建表和
- 下一篇: 单列变双列css_css 两列布局中单列