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

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

問題:

物品集合s={1,2,3,4,…,n},物品i的重量為wi,其價值為vi,揹包的容量(最大載重量)為W,如何裝使物品價值最大。(物品不能分割)

分析:

p(i,j)是揹包容量為j,可選物品為i(i+1,…,n)時的最優解 (“將前i個物品放入容量為j的揹包中”轉化為只考慮物品i)

  1. p(i,j)=p(i+1,j)表示沒裝入第i個物品或者第i個物品的重量wi超過揹包容量j。
  2. p(i,j)=p(i+1,j-wi)+vi表示裝入物品i,價值增加vi,揹包容量變為j-wi。
  3. 最後一個物品p(n,j),j大於wn,則能裝入,價值增加vn,否則不能裝入,價值增加0。

程式碼:

最優值:

#include<bits/stdc++.h>
using namespace std;
#define num 100//物品數量
#define num1 1000//揹包容量
int w[num];//物品重量
int v[num];//物品價值
int p[num][num1];//遞迴陣列

void value(int c,int n){//c表示揹包容量W,n是物品數量
    //邊界
    int jMax=min(w[n]-1,c);
    //先將小的不可以裝的置為0,將大於w[n]可以裝的置為v[n]   
    for(int j=0;j<=jMAx;j++){
        p[n][j]=0;
    }
    for(int j=w[n];j<=c;j++){
        p[n][j]=v[n];
    }
    //計算遞推式
    for(int i=n-1;i>1;i--){
        jMax=min(w[i]-1,c);
        for(int j=0;j<=jMax;j++){
            p[i][j]=p[i+1][j];
        }
        for(int j=w[i];j<=c;j++){
            p[i][j]=max(p[i+1][j],p[i+1][j-w[i]+v[i]);
        }
    }
    p[1][c]=p[2][c];
    if(c>=w[1]){
        p[1][c]=max(p[1][c],p[2][c-w[1]+v[1]);
    }
}

最優解:

//用xi=0或1表示物品i裝入或不裝入揹包
void jie(int c,int n,int x[]){
    for(int i=1;i<n;i++){
        if(p[i][c]==p[i+1][c]){
            x[i]=0;
        }
        else{
            x[i]=1;
            c=c-w[i];
        }
        x[n]=(p[n][c])?1:0;
    }
}

主函式:

int main(){
    int x[num];
    int W;
    cin>>W;
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>w[i]>>v[i];
    }
    memset(p,0,sizeof(p));
    value(W,n);
    cout<<p[1][W]<<endl;
    jie(W,n,x);
    for(int i=1;i<=n;i++){
        if(x[i]){
            cout<<i<<endl;
        }
    }
    return 0;
}

測試:

在這裡插入圖片描述