005多重揹包問題||

♚ Painting發表於2020-12-25

題目描述:

有 N種物品和一個容量是 V 的揹包。

第 i種物品最多有 si 件,每件體積是 vi,價值是 wi。

求解將哪些物品裝入揹包,可使物品體積總和不超過揹包容量,且價值總和最大。 輸出最大價值。

輸入格式:

第一行兩個整數,N,V用空格隔開,分別表示物品種數和揹包容積。

接下來有 N 行,每行三個整數 vi,wi,si用空格隔開,分別表示第 i種物品的體積、價值和數量。

輸出格式:

輸出一個整數,表示最大價值。

資料範圍:

0<N≤10000 0<V≤20000 0<vi,wi,si≤20000
提示: 本題考查多重揹包的二進位制優化方法。

輸入樣例:

4 5 1 2 3 2 4 1 3 4 3 4 5 2

輸出樣例:

10

這個題和多重揹包問題|中的題目描述是完全一樣的,不同的是資料範圍變大了,顯然我們要在原來的做法上做優化。
在解決這個問題之前,我們先來思考一下這個問題,如何把一個數s,拆成用最少的數表示呢?
解決辦法是:數s,s-1-2-4-8-…一直減到為負數為止,把這個數和前面的2的次冪數拿過來,這些數的組合可以表示0~s以內的任意數
同樣這個揹包問題,我們可以把物品拆成2的次冪份

#include<cstring>
#include<algorithm>
#include<vector>
#include<cstdio>
using namespace std;
int dp[2010];
//因為不知道最後選出了多少物品,所以定義一個結構體和一個vector容器
struct Good
{
    int v,w;
};
int main()
{
    vector<Good> goods;
    int N,V;
    cin>>N>>V;
    int v,w,s;
    int i,j,k;
    for(i=0;i<N;i++){
        cin>>v>>w>>s;
        for(k=1;k<=s;k*=2){//拆成0-1揹包問題
            s-=k;
            goods.push_back({v*k,w*k});
        }
        if(s>0) goods.push_back({v*s,w*s});
    }
    //這裡就可以用0-1揹包問題了
    for(auto good: goods){//auto遍歷vector容器,good就相當於goods[i]
        for(int j=V;j>=good.v;j--){
            dp[j] = max(dp[j],dp[j-good.v]+good.w);
        }
    }
    cout<<dp[V];
    return 0;
}

相關文章