背包问题

0-1背包

描述:

  有N件物品和一个容量为M的背包。第i件物品的重量是w[i],价值是v[i]。求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。

思路:

用子问题定义状态:即 f[i][j] f [ i ] [ j ] 表示前i件物品恰放入一个容量为j的背包可以获得的最大价值。

  • j<w[i] j < w [ i ] 时,表示 w[i] w [ i ] 不能放入,此时 f[i][j]=f[i1][j] f [ i ] [ j ] = f [ i − 1 ] [ j ]
  • j>w[i] j > w [ i ] 时,表示 w[i] w [ i ] 可以放入,此时分为两种情况,且对应的最大价值分别为:
    • 放: f[i][j]=f[i1][jw[i]]+v[i] f [ i ] [ j ] = f [ i − 1 ] [ j − w [ i ] ] + v [ i ]
    • 不放: f[i][j]=f[i1][j] f [ i ] [ j ] = f [ i − 1 ] [ j ]
    • 综上:则最终状态转移方程是: f[i][j]=max{f[i1][j],f[i1][jw[i]]+v[i]} f [ i ] [ j ] = max { f [ i − 1 ] [ j ] , f [ i − 1 ] [ j − w [ i ] ] + v [ i ] }
#include<iostream>

using namespace std;

int main(){

    int w[6]={0,2,2,6,5,4};
    int v[6]={0,6,3,5,4,6};
    int m[6][11];
    int M=10;
    int N=5;
    int res[5];

    // 状态转移方程
    for(int i=1;i<=N;i++){
        for(int j=1;j<=M;j++){
            if(j>w[i])
                m[i][j]=max(m[i-1][j-w[i]]+v[i],m[i-1][j]);
            else
                m[i][j]=m[i-1][j];
        }
    }
    // 判断选择哪个物品
    for(int i=N;i>1;i--){
        if(m[i][M]==m[i-1][M])
            res[i]=0;
        else{
            res[i]=1;
            c-=w[i];
        }
        res[1]=(m[1][M]>0)?1:0;
    }
    // 输出最大值矩阵
    for(int i=1;i<=N;i++){
        for(int j=1;j<=M;j++)
            cout<<m[i][j]<<' ';
        cout<<endl;
    }
    // 输出选择结果
    for(int i=1;i<=N;i++){
        cout<<res[i]<<' ';
    }
}

完全背包

描述:

  有N种物品和一个容量为M的背包,每种物品个数不限。第i种物品的重量是w[i],价值是v[i]。求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。

思路:

完全背包问题转化为0-1背包:虽然每种物品的个数不限,但是由于背包体积的限制,每种物品最大量为 k=Mw[i] k = M w [ i ] ,即可以看做有k个重量价值均相同的物品供挑选,将上述问题转为0-1背包,但是相当于加了一层内循环,时间复杂度 O(NM(jw[i])) O ( N M ∑ ( j w [ i ] ) ) ,空间复杂度 O(NM) O ( N M )

for(int i=1;i<N;i++){
        for(int j=1;j<=M;j++){
            for(int k=0;k<=M/w[i]];k++){
                if(j>=k*w[i]){
                    m[i][j]=max(m[i-1][j],m[i-1][j-k*w[i]]+k*v[i]);
                }
            }
        }
    }
简单优化:

  若两件物品满足 w[i]>w[j] w [ i ] > w [ j ] && v[i]<v[j] v [ i ] < v [ j ] ,则可以过滤掉物品i。

优化时间复杂度为O(NM):

  定义 m[i][j] m [ i ] [ j ] 表示在前i种物品中选择若干件物品放入容量为i的背包的最大价值。对于第i种物品的出现,我们对第i种物品进行决策。如果选择不放,则 m[i][j]=m[i1][j] m [ i ] [ j ] = m [ i − 1 ] [ j ] ;如果选择放,则背包中应该至少出现一件物品i,故 m[i][j]=m[i][jw[i]]+v[i] m [ i ] [ j ] = m [ i ] [ j − w [ i ] ] + v [ i ] 。因为 m[i][jw[i]] m [ i ] [ j − w [ i ] ] 中可能有第i种物品,也有可能没有,因此要预留 w[i] w [ i ] 的空间来存放第i种物品。

 for(int i=1;i<N;i++){
     for(int j=1;j<=M;j++){
         m[i][j]=m[i-1][j];
         if(j>=w[i]){
             m[i][j]=max(m[i][j],m[i][j-w[i]]+v[i]);
         }
     }
  }

多重背包

描述:

  有N种物品和一个容量为M的背包。第i种物品的重量是w[i],价值是v[i],个数为n[i]。求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。

思路1:

  同完全背包的第一个思路,只是在第三重循环中,对k进行约束。

for(int i=1;i<=N;i++){
        for(int j=1;j<=M;j++){
            for(int k=0;k<=M/w[i]&&k<=n[i];k++){
                if(j>=k*w[i]){
                    m[i][j]=max(m[i-1][j],m[i-1][j-k*w[i]]+k*v[i]);
                }
            }
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值