我的笔试题记录:0-1背包问题

这篇博客记录了0-1背包问题的详细解题过程,通过动态规划求解在限定总重量内获得最大价值的物品组合。介绍了动态规划的状态定义、转移方程,并给出了C++代码实现。此外,还探讨了如何根据动态规划数组找到所选物品的编号。
摘要由CSDN通过智能技术生成

题目

给定一组多个( n n n)物品,每种物品都有自己的重量( w i w_i wi)和价值( v i v_i vi),在限定的总重量/总容量( C C C)内,选择其中若干个(也即每种物品可以选0个或1个),求能取得的最大价值。

用更抽象的话说,给定正整数 1 ≤ w i ≤ n , 1 ≤ v i ≤ n , 1 ≤ C ≤ n 1 \leq w_i \leq n, 1 \leq v_i \leq n, 1 \leq C \leq n 1win,1vin,1Cn,求:
m a x ∑ i = 1 n x i v i , s . t . ∑ i = 1 n x i w i ≤ C , x ∈ { 0 , 1 } max\sum_{i=1}^nx_iv_i, s.t.\sum_{i=1}^nx_iw_i \leq C, x \in \{0, 1\} maxi=1nxivi,s.t.i=1nxiwiC,x{0,1}

解法

动态规划

定义 d p ( i , w ) dp(i, w) dp(i,w)为在前i个物品中筛选出总重量不超过w的最大价值。

转移方程:

对于第i个物品,有两种做法:

  • 把它放入背包,则 d p ( i , w ) = d p ( i − 1 , w − w i ) + v i dp(i, w)=dp(i-1, w-w_i)+v_i dp(i,w)=dp(i1,wwi)+vi,即先在 i − 1 i-1 i1个物品中以最大价值筛选出总重量不超过 w − w i w-w_i wwi的物品,再加上第 i i i个物品的价值 v i v_i vi
  • 不把它放入背包,则 d p ( i , w ) = d p ( i − 1 , w ) dp(i, w)=dp(i-1, w) dp(i,w)=dp(i1,w),即在负重为 w w w时,从 i i i个物品中筛选和从 i − 1 i-1 i1个物品中筛选的结果是一样的,因为第i个不放入背包中。

w i > w w_i>w wi>w时,第 i i i个物品不可能放入背包中,此时 d p ( i , w ) dp(i, w) dp(i,w)只能取第二种做法。当 w i ≤ w w_i \leq w wiw时,需要比较两种做法哪一种能获取较高的价值。得到转移方程:

d p ( i , w ) = { 0 i = 0 0 w = 0 d p ( i − 1 , w ) w i > w m a x { d p ( i − 1 , w − w i ) + v i , d p ( i − 1 , w ) } o t h e r w i s e dp(i, w)= \begin{cases} 0 & i=0 \\ 0 & w=0 \\ dp(i-1, w) & w_i>w \\ max\{dp(i-1, w-w_i)+v_i, dp(i-1, w)\} & otherwise \end{cases} dp(i,w)=00dp(i1,w)max{dp(i1,wwi)+vi,dp(i1,w)}i=0w=0wi>wotherwise

代码(C++):

/**
 * @param itemCount 物品个数
 * @param weight 物品重量,weight[i]表示第i个物品的重量(i从1开始)
 * @param value 物品价值,value[i]表示第i个物品的价值(i从1开始)
 * @param maxLoad 背包的最大负重
 */
int maxValue(int itemCount, vector< int > weight, vector< int > value,
             int maxLoad)
{
  vector< vector< int > > dp(itemCount + 1, vector< int >(maxLoad + 1, 0));
  // dp[i][j]表示从第1个到第i个物品中选择不超过重量j的最大总价值

  for (int i = 1; i <= itemCount; i++) {
    for (int j = 1; j <= maxLoad; j++) {
      if (weight[i] > j)
        dp[i][j] = dp[i - 1][j]; // 不选
      else {
        int temp1 = dp[i - 1][j];
        int temp2 = dp[i - 1][j - weight[i]] + value[i];
        dp[i][j] = temp1 > temp2 ? temp1 : temp2;
      }
    }
  }

  return dp[itemCount][maxLoad];
}

扩展

通过这个dp数组求出选择的物品编号:

i = n , j = C i=n, j=C i=n,j=C开始看,若 d p ( i − 1 , j ) = d p ( i , j ) dp(i-1, j) = dp(i, j) dp(i1,j)=dp(i,j),则表示第 i i i个物品没有被选择,则从 d p ( i − 1 , j ) dp(i-1, j) dp(i1,j)继续寻找。如果 d p ( i − 1 , j ) ≠ d p ( i , j ) dp(i-1,j) \neq dp(i, j) dp(i1,j)=dp(i,j),则表示第 i i i个物品被选择,下一步从 d p ( i − 1 , j − w i ) dp(i-1, j-w_i) dp(i1,jwi)继续寻找。

/**
 * @param dp 求最大价值时产生的动态规划数组dp
 * @param weight 物体重量数组
 * @param itemCount 物体个数
 * @param maxLoad 背包最大负重
 */
vector< int > findItems(vector< vector< int > > dp, vector< int > weight,
                        int itemCount, int maxLoad)
{
  int i = itemCount, j = maxLoad;
  vector< int > res;
  while (i > 0 && j > 0) {
    if (dp[i - 1][j] < dp[i][j]) {
      res.push_back(i);
      i--;
      j -= weight[i];
    } else {
      i--;
    }
  }
  return res;
}

本片记录总结自:
【动态规划】01背包问题 - 弗兰克的猫 - 开发者的网上家园
0-1背包问题的动态规划算法 - 知乎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值