背包问题-动态规划

本文详细介绍了背包问题的几种类型,包括01背包、完全背包、多重背包、混合背包,以及它们的动态规划解决方案。针对每种背包问题,阐述了问题描述、解答思路,并提供了示例代码和优化方法,帮助理解如何通过动态规划找到价值最大化的物品选择策略。
摘要由CSDN通过智能技术生成

背包问题九讲
背包问题参考博客

介绍:

背包问题:有一系列的物品,每个物品有自己的价值v重量w,怎么选取物品放到有一定最大承受重量m的背包里面达到价值最大的问题。

  1. 如果每件物品只有一件:01背包
  2. 如果每件物品可以拿无数次:完全背包
  3. 如果第 i 种商品有 n 件:多重背包
  4. 如果将01背包、完全背包、多重背包混合起来。也就是说,有的物品只可以取一次(01背包),有的物品可以取无限次(完全背包),有的物品可以取的次数有一个上限(多重背包): 混合背包
  5. 二维费用背包问题
  6. 分组背包问题
  7. 有依赖的背包问题



背包问题

01背包问题

  • 问题描述:一共有 i 种商品,每种商品的价值和重量是 v[i] w[i],怎么选取放到总容量是M的背包中以达到最大价值,每种商品只有一个。

  • 问题解答思路:

假设 f [ i , m ] f [ i , m ] f[i,m] 就设为从前 i 种物品中选择放入当前容量为 m 的背包最大的价值。那么状态转移方程为:
f [ i , m ] = m a x { f [ i − 1 , m ] ,    f [ i − 1 , m − w [ i ] ] + v [ i ] } f [ i , m ]=max \left \{ f[i-1,m], \space \space f[i-1,m-w[i]]+v[i] \right \} f[i,m]=max{ f[i1,m],  f[i1,mw[i]]+v[i]}
解释:考虑子问题,将第i种商品放入背包:
  自顶向下的思路   : f [ i , m ] = { f [ i − 1 , m ] :第i种物品不放 f [ i − 1 , m − w [ i ] ] + v [ i ] :放第i种物品 \textbf{ 自顶向下的思路 }: f[i,m]=\left\{ \begin{aligned} &f[i-1, m] \text{:第i种物品不放}\\ & f[i-1,m-w[i]]+v[i] \text{:放第i种物品} \end{aligned} \right.  自顶向下的思路 f[i,m]={ f[i1,m]:第i种物品不放f[i1,mw[i]]+v[i]:放第i种物品
如果第 i 种商品不放,那么最大价值就等于将前i-1种商品放入 容量为 m的背包;如果放第 i 种物品(前提是当前容量m > w[i]),则最大价值:将前 i-1 种商品放到 容量为 m-w[i]的最大价值+当前的v[i]


  • 例子:

背包的容量 M= 8

商品 重量 价值
1 2 3
2 3 4
3 4 5
4 5 6

示例代码:

int main(int argc, char* argv[])
{
   
    int w[5] = {
   0, 2, 3, 4, 5};
    int v[5] = {
   0, 3, 4, 5, 6};
    int M = 8;
    int dp[5][9];
    memset(dp, 0, sizeof(dp));

    for(int i=1; i<=4; i++) // 四种商品
    {
   
		for(int j=1; j<=M; j++) //包的容量
		{
   
		    if(j<w[i]) // 当前的容量 < 当前的物品重量
				dp[i][j] = dp[i-1][j]; // 不放当前第 i 种商品 
		    else // 放第i种商品,取放与不放的最大值
				dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]]+v[i]);
		}
    }

    // 输出动态规划表
    for (int i = 0; i < 5; i++) 
    {
   
		for (int j = 0; j < 9; j++) 
		{
   
		    cout << dp[i][j] << ' ';
		}
		cout << endl;
    }

    return 0;
}

结果:4种商品,背包容量8的最优解是:10
在这里插入图片描述

  • 空间复杂度的优化
    有n种商品,背包容量为M,以上方法的时间和空间复杂度均为O(NM),时间复杂度没法优化,但是空间复杂度依然可以优化到O(M),实际可以用一维数组 dp[m+1] 即可。外层循环的每次 i 初始值是上一轮递推得到的值(i - 1时的情况),需要注意的是我们必须倒序跟新, 这样才能保证推dp[j]时dp[j-w[i]]保存的是状态dp[i-1][j-w[i]] 的值,这样符合状态转移方程
int main(int argc, char* argv[])
{
   
    int w[5] = {
   0, 2, 3, 4, 5};
    int v[5] = {
   0, 3, 4, 5, 6};
    int M = 8;
    int dp[9];
    memset(dp, 0, sizeof(dp));

    for(int i=1; i<=4; i++) // 四种商品
    {
   
		for(int j=M; j>0; j--) //包的容量
		{
   
		    if(j>=w[i]) // 当前的容量 >= 当前的物品重量
			dp[j] = max(dp[j], dp[j-w[i]]+v[i]);
		}
    }

    // 输出动态规划表
    for (int j = 0; j < 9; j++) 
    {
   
		cout << dp[j] << ' ';
    }

    return 0;
}

结果:
在这里插入图片描述

  • 01背包拓展:
  1. 求取的不是价值的最大值而是最小值:把max换成min即可,原理相同。
  2. 求最优解有哪些物品:背包问题最优解回溯
  3. 要求“恰好装满背包”时的最优解背包九讲的01背包-初始化细节问题例子,可以初始化 -999,有可能最后没有满足要求的方案,非法值就是 -大数
  4. 如果碰到物品的v[i]是带有负数或者小数,动态规划的数组申请为1维的,如果连重量w[i]是小数,申请为1维的动态规划的数组也不满足要求,因为小数没办法作为数组的下标:上面代码的 dp[j-w[i]],我们可以选择将背包容量和每个物品的重量同时乘以大数满足整数要求。


完全背包

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

  • 问题解答思路:在选取第 i 种商品的时候,可以选 0, 1, 2 … n , 只要保证 n个商品的重量 <= 当前背包能承受的重量,所以按照01背包的假设:F[i][m]代表前i种商品放到容量m的背包中的最大价值,那么状态转移方程:
    f [ i ] [ m ] = m a x { f [ i − 1 ] [ m − k ∗ w [ i ] ] + k ∗ v [ i ] }        k ∗ w [ i ] < = m f[i][m] = max \left \{ f[i-1][m-k*w[i]] +k*v[i]\right \} \space \space \space \space \space \space k*w[i]<=m

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值