1. 程式人生 > >動態規劃揹包問題

動態規劃揹包問題

1.01揹包

問題:

有N件物品和一個容量為V的揹包。第i件物品的費用是c[i],價值是w[i]。

求解將哪些物品裝入揹包可使這些物品的費用總和不超過揹包容量,且價值總和最大。

分析:

用dp[i][v]表示將前i個物品放入價值為v的揹包能獲得的最大價值

計算dp[i][v]時,分兩種情況:第i件物品選或者不選,二者中價值較大的值就是f[i][v]的值,

即:dp[i][v]=max{dp[i-1][v],dp[i-1][v-c[i]]+w[i]}

注意: 矩陣c和w的第一位要空出來,因為i == 0的情況被跳過,之後的演算法也是同理。

    vector<int> c = {0, 1, 2, 3}; // 費用
    vector<int> w = {0, 3, 2, 1}; // 價值
    int N = c.size()-1;
    int V = 2;
    vector<vector<int>> dp(N+1, vector<int>(V+1));
    for(int i = 0; i <= N; i++)
    for(int v = 0; v <= V; v++) {
        if(i == 0)
            dp[i][v] = 0;
        else if(v < c[i])              // 剩餘空間不足選擇第i件
            dp[i][v] = dp[i-1][v];
        else
            dp[i][v] = max(dp[i-1][v], dp[i-1][v-c[i]]+w[i]);
    }
    cout << dp[N][V] << endl;

考慮減少空間的開銷,由於計算過程為從上到下,從左到右,dp[i][v]實際上只依賴於dp[i-1][0...v]這些值。如果使用從右到左的順序,就可以只使用一維陣列進行計算。

    vector<int> dp2(N+1);
    for(int i = 0; i <= N; i++)
    for(int v = V; v >= 0; v--) {
        if(i == 0)
            dp2[v] == 0;
        else if(v >= c[i])
            dp2[v] = max(dp2[v], dp2[v-c[i]]+w[i]);  // 一維矩陣實現
    }

2.完全揹包

問題:

有N種物品和一個容量為V的揹包,每種物品都有無限件可用。第i種物品的費用是c[i],價值是w[i]。

求解將哪些物品裝入揹包可使這些物品的費用總和不超過揹包容量,且價值總和最大

分析:

用dp[i][v]表示將前i個物品放入價值為v的揹包能獲得的最大價值

計算dp[i][v]時,也是可以分為兩種情況:第i件物品選n件(n>=1)或者不選,

也即: dp[i][v] = max(dp[i-1][v], dp[i][v-c[i]]+w[i])

需要注意,當選定第i件物品時,標號還是為i,因為選定之後還可以繼續選i

    vector<vector<int>> dp(N+1, vector<int>(V+1));
    for(int i = 0; i <= N; i++)
    for(int v = 0; v <= V; v++) {
        if(i == 0)
            dp[i][v] = 0;
        else if(v < c[i])              // 剩餘空間不足選擇第i件
            dp[i][v] = dp[i-1][v];
        else
            dp[i][v] = max(dp[i-1][v], dp[i][v-c[i]]+w[i]);
    }

同樣,完全揹包也可以進行減少空間開銷。相對於01揹包,只需要修改v遍歷的順序就可以了。原因是:在01揹包中,倒序的目的是保證決策是否選擇i時依據的是沒有選入i的子結果,而在完全揹包中,我們正需要選入i的子結果,所以他們的順序時相反的。

    vector<int> dp2(N+1);
    for(int i = 0; i <= N; i++)
    for(int v = 0; v <= V; v++) {
        if(i == 0)
            dp2[v] == 0;
        else if(v >= c[i])
            dp2[v] = max(dp2[v], dp2[v-c[i]]+w[i]);  // 一維矩陣實現
    }

3.  多重揹包

問題:

有N種物品和一個容量為V的揹包。第i種物品最多有n[i]件可用,每件費用是c[i],價值是w[i]。求解將哪些物品裝入揹包可使這些物品的費用總和不超過揹包容量,且價值總和最大。

分析:

簡單地,可以將第i個物品分為n[i]個物品來考慮,然後使用01揹包的演算法解決問題。但是這樣會大量增加物品的數量,複雜度比較高。可以考慮將物品用二進位制進行劃分,分為:1, 2, 4, ..., 2^{k-1}, n[i]-(2^{k}-1),其中2^{k}-1 < n[i] (2^{k}-1也就是前面k項的和,它當然得比總的值要小)。因為1, 2, 4, ..., 2^{k-1}可以表示任意不大於2^{k-1}的數,所以再加上n[i]-(2^{k}-1),就可以表示所有不大於n[i]的數了。

int main() {
    vector<int> c = {1, 1, 1}; // 費用
    vector<int> w = {1, 2, 3}; // 價值
    vector<int> num = {5, 10, 15};
    int N = c.size();
    int V = 20;
    vector<int> C,W;
    C.push_back(0);
    W.push_back(0);  // 第一個值置空
    for(int i = 0; i < N; i++) {
        for(int k = 1; num[i] > 0;k*=2) {
            if(k > num[i])
                k = num[i];
            num[i] -= k;
            C.push_back(k*c[i]);
            W.push_back(k*w[i]);
        }
    }
    N = C.size()-1;
    vector<int> dp(V+1);
    for(int i = 0; i <= N; i++)
    for(int v = V; v >= 0; v--) {
        if(i == 0)
            dp[v] == 0;
        else if(v >= C[i])
            dp[v] = max(dp[v], dp[v-C[i]]+W[i]);  
    }
    cout << dp[V] << endl;    
}

其中中隱含了對指數的判斷,簡化了過程。之後再對C,W進行01揹包的處理就行了。

4. 混合揹包

問題:

有的物品只可以取一次,有的物品可以取無限次,有的物品可以取的次數有一個上限。

分析:

這個問題實際上也就是前三個問題的疊加,首先還是使用二進位制拆分的方式將只可以取有限次數的物品轉變為只可以取一次的物品(多重揹包問題),然後剩下的就是01揹包和完全揹包的混合,因為二者的實現只是在v的遍歷順序上有區別,只需要對物品i進行判斷,對01揹包進行逆序遍歷,完全揹包進行順序遍歷即可。

    vector<int> dp(V+1);
    for(int i = 0; i <= N; i++) {
        if(complete.find(i) != complete.end()) {
            for(int v = 0; v <= V; v++) {
                if(i == 0)
                    dp[v] == 0;
                else if(v >= C[i])
                    dp[v] = max(dp[v], dp[v-C[i]]+W[i]);  
            }
        } else {
            for(int v = V; v >= 0; v--) {
                if(i == 0)
                    dp[v] == 0;
                else if(v >= C[i])
                    dp[v] = max(dp[v], dp[v-C[i]]+W[i]);  
            }            
        }
    }

5. 二維揹包

問題:

二維費用的揹包問題是指:對於每件物品,具有兩種不同的費用;選擇這件物品必須同時付出這兩種代價;對於每種代價都有一個可付出的最大值(揹包容量)。問怎樣選擇物品可以得到最大的價值。設這兩種代價分別為代價1和代價2,第i件物品所需的兩種代價分別為a[i]和b[i]。兩種代價可付出的最大值(兩種揹包容量)分別為V和U。物品的價值為w[i]。

分析:

多了一個條件,新增一維即可,同01揹包。

    int dp[N+1][V1+1][V2+1] = {0};
    for(int i = 0; i <= N; i++)
    for(int v1 = 0; v1 <= V1; v1++) 
    for(int v2 = 0; v2 <= V2; v2++){
        if(i == 0)
            dp[i][v1][v2] = 0;
        else if(v1 < c1[i] || v2 < c2[i]) {
            dp[i][v1][v2] = dp[i-1][v1][v2];
        }
        else
            dp[i][v1][v2] = max(dp[i-1][v1][v2], dp[i-1][v1-c1[i]][v2-c2[i]]+w[i]);
    }
    cout << dp[N][V1][V2] << endl;

6. 分組揹包

問題:

有N件物品和一個容量為V的揹包。第i件物品的費用是c[i],價值是w[i]。這些物品被劃分為若干組,每組中的物品互相沖突,最多選一件。求解將哪些物品裝入揹包可使這些物品的費用總和不超過揹包容量,且價值總和最大。

分析:

可以這樣考慮,對於每個組,可以分為兩種情況:選這一組中的物品,不選這一組中的物品

即: dp[k][v]=max{dp[k-1][v],dp[k-1][v-c[i]]+w[i]

    vector<vector<int>> c = {{},{1,1,1}}; // 費用
    vector<vector<int>> w = {{},{1,2,3}}; // 價值
    int N = c.size()-1;
    int V = 5;
    vector<vector<int>> dp1(N+1, vector<int>(V+1,0));
    for(int k = 0; k <= N; k++)
    for(int v = 0; v <= V; v++) {
        if(k == 0)
            dp1[k][v] == 0;
        else {
            for(int i = 0; i < c[k].size(); i++){
                if(v >= c[k][i])
                    dp1[k][v]=max(dp1[k-1][v],dp1[k-1][v-c[k][i]]+w[k][i]);
            }
        }
    }
    cout << dp1[N][V] << endl;

也可以使用一維陣列減少空間的開銷

    vector<int> dp(N+1, 0);
    for(int k = 0; k <= N; k++)
    for(int v = V; v >= 0; v--) 
    for(int i = 0; i < c[k].size(); i++){
        if(v >= c[k][i])
            dp[v]=max(dp[v],dp[v-c[k][i]]+w[k][i]);
    }
    cout << dp[V] << endl;

相關推薦

經典動態規劃——揹包問題系列一

經典動態規劃——揹包問題系列一 複賽前發一波部落格,雖然意義不是很大了…… 本篇講的是揹包問題基礎 01揹包問題 簡述 有N件物品和一個容量為V的揹包。第i件物品的體積是c[i],價值是w[i]。求解將哪些物品裝入揹包可使這些物品的費用總和不超過揹包容量,且價值總和最大。 思路 動態規劃的基本題

動態規劃-揹包問題(跳躍點解法)

  對於 0-1 揹包問題,DP的解法很普遍。還有一種“跳躍點”的解法,該方法的提出,是根據揹包求解過程中的記錄表 v(i,j)的函式性特點而來的。(v(i,j)表記錄的是前 i 種物品,達到總重量 j 時的最大利益) 可以Dp 求解一下,然後列印一下表進行觀察,也可以根據這個

[洛谷]P2871 [USACO07DEC]手鍊Charm Bracelet (#動態規劃 -揹包 -1.12)

題目描述 Bessie has gone to the mall's jewelry store and spies a charm bracelet. Of course, she'd like to fill it with the best charms possible from t

[洛谷]P2370 yyy2015c01的U盤 (#二分答案 -1.1)(#動態規劃 -揹包 -1.11)

題目背景 在2020年的某一天,我們的yyy2015c01買了個高階U盤。 題目描述 你找yyy2015c01借到了這個高階的U盤,拷貝一些重要資料,但是你發現這個U盤有一些問題: 1、這個U盤的傳輸介面很小,只能傳輸大小不超過L的檔案 2、這個U盤容量很小,一共只能裝不超過

動態規劃——揹包問題1:01揹包

揹包問題是動態規劃中的一個經典題型,其實,也比較容易理解。 當你理解了揹包問題的思想,凡是考到這種動態規劃,就一定會得很高的分。 揹包問題主要分為三種: 01揹包    完全揹包    多重揹包 其中,01揹包是最基礎的,最簡單的,也是最重要的。 因為其他兩個揹包都是由01揹包演變而來的。所以,學好01揹

動態規劃: 揹包問題(施工中)

參考自如下兩篇部落格: http://www.cnblogs.com/tanky_woo/archive/2010/07/31/1789621.html http://blog.csdn.net/insistgogo/article/details/11081025 一、

c++動態規劃——揹包問題

問題基礎:有N件物品和一個容量為C的揹包。第i件物品的體積是W[i],價值是V[i]。求解將哪些物品裝入揹包可使這些物品的重量總和不超過揹包容量,且價值總和最大。 怎樣才能得到放入書包物品的的最大價值呢? 解決方法——【動態規劃】 運籌學的分支,糾結決策過程最優化的數學方法

動態規劃揹包問題

1.01揹包 問題: 有N件物品和一個容量為V的揹包。第i件物品的費用是c[i],價值是w[i]。 求解將哪些物品裝入揹包可使這些物品的費用總和不超過揹包容量,且價值總和最大。 分析: 用dp[i][v]表示將前i個物品放入價值為v的揹包能獲得的最大價值 計算dp

動態規劃/揹包】整數劃分的5種情況

1.將n劃分成若干正整數之和的劃分數(可以存在相同整數)。 轉移方程如下: dp[n][m]=dp[n][m-1]+ dp[n-m][m]  dp[n][m]表示整數 n 的劃分中,每個數不大於 m

動態規劃-揹包

(一)01揹包 有個揹包,可以裝重為V的物品。有n種物品,第i個物品的重量(或說是空間)是v[i], 價值是c[i],每種物品只有一件,求能裝的最大價值 二維解法: 定義二維陣列f[i][j],表示前

動態規劃-揹包問題、兌換零錢問題、旅行商問題

揹包問題 問題介紹 假定需要將Goods={g1,g1,…gn}放入容量為ALL的揹包內,volumej代表第i個物品的體積,valuej代表第j個物品的價值。我們要把這些物品裝進揹包

動態規劃-揹包模板

——>主題 this is code! #include<bits/stdc++.h> using namespace std; const int N = 10000; int dp[N]; int c[N],w[N],num[N];/

採藥-動態規劃(01揹包

採用一維陣列進行優化 #include<cstdio> #include<algorithm> using namespace std; int w[105], v[105]; int dp[1005]; int main() { int m, n; sca

0-1揹包問題(動態規劃

http://acm.hdu.edu.cn/showproblem.php?pid=1171 這道題咋看有點複雜,其實也只是換了一種思維,因為題目要求要儘量平均分配,所以我們可以先將總價值sum求出,然後得出其分配的平均值為sum/2,要注意這個答案可能為小數,但是又因為sum是整數,所以最

動態規劃-0-1揹包問題

給定n個物品和一個揹包。物品i的重量為wi,價值為vi,揹包容量為c。問如何選擇裝入揹包中的物品,使得裝入揹包的物品的價值最大?在裝入揹包時,每種物品i只有兩種選擇,裝入或者不裝入,既不能裝入多次,也不能只裝入一部分。 因此,此問題稱為0-1揹包問題.0-1揹包問題是一個特殊的整數規劃問題。

利用動態規劃演算法解01揹包問題->二維陣列傳參->cpp記憶體管理->堆和棧的區別->常見的記憶體錯誤及其對策->指標和陣列的區別->32位系統是4G

1、利用動態規劃演算法解01揹包問題 https://www.cnblogs.com/Christal-R/p/Dynamic_programming.html 兩層for迴圈,依次考察當前石塊是否能放入揹包。如果能,則考察放入該石塊是否會得到當前揹包尺寸的最優解。 // 01 knap

POJ-2184 Cow Exhibition 【動態規劃DP+01揹包變換】

題目傳送門 題目:共有N頭牛,接下來N行是每頭牛的智商和情商,從這些牛中任意選取若干頭牛,使得牛的智商和+情商和最大,同時智商和(TS),情商和(TF)都不小於0。 題解:以智商作為容量,求前i頭牛在智商為j的情況下的最大情商 。因為有負數,所以容量擴大100000,修改dp陣

HDU-2844 Coins 【動態規劃DP+多重揹包

題目傳送門 題目:有n種硬幣,第i種硬幣的價值為Ai,數目為Ci,求這些硬幣能配出1~m中的幾種價值。 題解:dp[j]表示是否能配出價值j。sum[i][j]表示第i種硬幣取到價值j時需要的數目。sum陣列可以壓掉i的那一維,每次都要記得清零。 AC程式碼: #include

POJ-2392 Space Elevator 【動態規劃DP+多重揹包

題目傳送門 題目:牛要去太空了!他們計劃通過建造一種太空升降機來達到軌道:一個巨大的積木塔。他們有K (1 <= K <= 400)不同型別的積木來建造塔。型別i的每個塊的高度都是h_i (1 <= h_i <= 100),並且數量上都是c_i (1 <= c_

動態規劃之完全揹包問題(java實現)

之前寫了01揹包問題,現在寫完全揹包問題。和01揹包不同的是,完全揹包不限定某種物品的件數,可以裝0,1,2,...,而01揹包只有裝與不裝的區別。但是思考問題的方式還是一樣的,我就其中的最大值。詳細程式碼和註釋見下面程式碼。 package backpack; /* f[i][v]:前i件物