多重背包問題


多重背包問題:

有N種物品和一個容量為V的背包。

第i種物品最多有n[i]件可用,每件費用是w[i],價值是c[i]。

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

簡明易懂,這就和完全背包問題差不多。

所以基本的方程只需將完全背包問題的方程略微修改即可,因為對於第i種物品有n[i]+1種策略:

取0件,取1件……取n[i]件。

令f[i][v]表示前i種物品恰放入一個容量為v的背包的最大權值,

則:f[i][v]=max{f[i-1][v-k*w[i]]+ k*c[i]|0<=k<=n[i]}。復雜度是O(V*∑n[i])。

另一種方法是轉化為01背包問題   

把第i種物品換成n[i]件01背包中的物品,則得到了物品數為∑n[i]的01背包問題,直接求解,復雜度仍然是O(V*∑n[i])。   

但是我們期望將它轉化為01背包問題之后能夠像完全背包一樣降低復雜度。

考慮二進制的思想,把第i種物品換成若干件物品,

使得原問題中第i種物品可取的每種策略——取0..n[i]件——均能等價於取若干件代換以后的物品。另外,取超過n[i]件的策略必不能出現。   

方法是:將第i種物品分成若干件物品,其中每件物品有一個系數,這件物品的費用和價值均是原來的費用和價值乘以這個系數。

使這些系數分別為 1,2,4,...,2^(k-1),n[i]-2^k+1,且k是滿足n[i]-2^k+1>0的最大整數(注意:這些系數已經可以組合出1~n[i]內的所有數字)。

例如,如果n[i]為13,就將這種物品分成系數分別為1,2,4,6的四件物品。   

分成的這幾件物品的系數和為n[i],表明不可能取多於n[i]件的第i種物品。

另外這種方法也能保證對於0..n[i]間的每一個整數,均可以用若干個系數的和表示,

這個證明可以分0..2^k-1和2^k..n[i]兩段來分別討論得出,只需要幾分鍾便可輕松解決。

有一道很相似的例題:

洛谷P1064

傳送門

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cctype>
#define ll long long
#define gc() getchar()
#define maxn 65
#define maxm 20005
using namespace std;

inline ll read(){    //日常讀入優化,對這道題毫無卵用
    ll a=0;int f=0;char p=gc();
    while(!isdigit(p)){f|=p=='-';p=gc();}
    while(isdigit(p)){a=(a<<3)+(a<<1)+(p^48);p=gc();}
    return f?-a:a;
}
void write(ll a){
    if(a>9)write(a/10);
    putchar(a%10+'0');
}

int n,m,v[maxn],w[maxn],f[maxm],c[maxn][maxn];  //c[i][0]表示主件為i的物品的數量,那么c[0][0]就表示主件的數量,c[i][j]表示主件為i的第j個物品的標號,這樣寫本意是處理無窮個配件,結果發現配件至多有兩個,懶得改了
int main(){
    m=read()/10;n=read();
    for(int i=1;i<=n;++i){
        v[i]=read()/10;w[i]=read()*v[i];
        int q=read();c[q][++c[q][0]]=i;
    }
    for(int i=1;i<=c[0][0];++i)
        for(int k=m;k>=v[i];--k){
            int v2=(k-v[c[0][i]])>=0?f[k-v[c[0][i]]]+w[c[0][i]]:0;    //可選則存為可存入的,不可選則為0,以下同此,v2,v3,v4,v5分別代表的是前面講的第二第三第四第五種情況
            int v3=(k-v[c[0][i]]-v[c[c[0][i]][1]])>=0?f[k-v[c[0][i]]-v[c[c[0][i]][1]]]+w[c[0][i]]+w[c[c[0][i]][1]]:0;    
            int v4=(k-v[c[0][i]]-v[c[c[0][i]][2]])>=0?f[k-v[c[0][i]]-v[c[c[0][i]][2]]]+w[c[0][i]]+w[c[c[0][i]][2]]:0;
            int v5=(k-v[c[0][i]]-v[c[c[0][i]][1]]-v[c[c[0][i]][2]])>=0?f[k-v[c[0][i]]-v[c[c[0][i]][1]]-v[c[c[0][i]][2]]]+w[c[0][i]]+w[c[c[0][i]][1]]+w[c[c[0][i]][2]]:0;
            f[k]=max(f[k],max(max(v2,v3),max(v4,v5)));  //簡化后應該就比較容易看懂了
        }
    write(f[m]*10);
    return 0;
}

第二種:

#include<bits/stdc++.h>
using namespace std;
int n,m;
int f[40000],g[40000];
struct obj
{
    int price,value,team;
}
a[100];
int main()
{
    cin>>n>>m;
    for(int i=1;i<=m;i++)
     scanf("%d%d%d",&a[i].price,&a[i].value,&a[i].team),a[i].value*=a[i].price;
     for(int i=1;i<=m;i++)
     if(!a[i].team){
         for(int j=1;j<a[i].price;j++)g[j]=0;
         for(int j=a[i].price;j<=n;j++)g[j]=f[j-a[i].price]+a[i].value;
         for(int j=1;j<=m;j++)
         if(a[j].team==i)
         for(int k=n;k>=a[i].price+a[j].price;k--)g[k]=max(g[k],g[k-a[j].price]+a[j].value);
        for(int j=a[i].price;j<=n;j++)f[j]=max(f[j],g[j]);
    }
    cout<<f[n];
    return 0;
}

 


免責聲明!

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



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