第二讲 完全背包问题

【题目来源】AcWing 3. 完全背包问题

解题思路
完全背包和 0 / 1 0/1 0/1背包的唯一区别在于,每一个物品变为了每一种物品且都有无数件,可以重复装入。

const int N = 1050;
struct res{
  int v, w;
} q[N];
int n, V, dp[N][N];

int main(){
  cin >> n >> V;
  for(int i = 1; i <=n ; i ++)  cin >> q[i].v >> q[i].w;
  return 0;
}

要注意,此时的状态转移方程会发生改变
动态规划思想:
d p [ i ] [ j ] dp[i][j] dp[i][j],代表当背包空间为 j j j 时,我面前有前 i i i 种物品,能够装入的最大价值,推导如下:
d p [ i ] [ j ] dp[i][j] dp[i][j]分为多种情况,在 d p [ i − 1 ] [ j ] dp[i - 1][j] dp[i1][j]的基础上:
①不装入或者说装入 0 0 0个第 i i i 种物品, d p [ i − 1 ] [ j ] dp[i - 1][j] dp[i1][j]
②装入 1 1 1个第 i i i 种物品, d p [ i − 1 ] [ j − q [ i ] . v ] + q [ i ] . w dp[i - 1][j - q[i].v] + q[i].w dp[i1][jq[i].v]+q[i].w
……
③装入 k k k个第 i i i 种物品, d p [ i − 1 ] [ j − k ∗ q [ i ] . v ] + k ∗ q [ i ] . w dp[i - 1][j - k * q[i].v] + k * q[i].w dp[i1][jkq[i].v]+kq[i].w
……

推式1:dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - v] + w, dp[i - 1][j - 2 * v] + 2 * w,, dp[i - 1][j - k * v] + k * w,)
推式2:dp[i][j - v] = max(dp[i - 1][j - v], dp[i - 1][j - 2 * v] + w, dp[i - 1][j - 3 * v] + 2 * w,, dp[i - 1][j - (k + 1)v] + k * w,)
推式2变形:dp[i][j - v] + w = max(dp[i - 1][j - v] + w, dp[i - 1][j - 2 * v] + 2 * w, dp[i - 1][j - 3 * v] + 3 * w,, dp[i - 1][j - (k + 1) * v] + (k + 1) * w,)
结论:dp[i][j] = max(dp[i - 1][j], dp[i][j - v] + w);
dp[i][j] = max(dp[i - 1][j], dp[i][j - q[i].v] + q[i].w);

亦可解释为,当背包空间为 j j j 时,有前 i i i 种物品可装入,但是我不知道现在想要装入的这前 i i i 种物品已经装入了几个,所以考虑空间为 j − q [ i ] . v j - q[i].v jq[i].v 时,前 i i i 种物品的装入情况,即 d p [ i ] [ j − q [ i ] . v ] dp[i][j - q[i].v] dp[i][jq[i].v]

for(int i = 1; i <= n; i ++)
  for(int j = 0; j <= V; j ++){
    dp[i][j] = dp[i - 1][j];
    if(j >= q[i].v)  dp[i][j] = max(dp[i - 1][j], dp[i][j - q[i].v] + q[i].w);
  }

优化为一位数组, d p [ i ] [ j ] dp[i][j] dp[i][j]的值取决于 d p [ i − 1 ] [ j ] dp[i - 1][j] dp[i1][j] d p [ i ] [ j − q [ i ] . v ] dp[i][j - q[i].v] dp[i][jq[i].v],即上一行同一列或同一行前列,数组前面的值会发生改变并且决定后面的值,所以应该从前往后遍历。

for(int i = 1; i <= n; i ++)
  for(int j = q[i].v; j <= V; j ++)  //从前往后遍历
    dp[j] = max(dp[j], dp[j - q[i].v] + q[i].w);
cout << dp[V];
  • 18
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值