数据结构背包问题c语言思路,【数据结构与算法】背包问题总结梳理

背包问题总结分析

背包问题是个很经典的动态规划问题,本博客对背包问题及其常见变种的解法和思路进行总结分析

01背包

问题介绍

有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。

第 i 件物品的体积是 v[i],价值是 w[i]。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。

基本思路

定义int[][] dp,dp[i][j] 表示当容量为j时,对于前i个物品而言的最优放置策略(即最大价值)。对于物品 i 而言,只有放与不放,这两种选择。因此可以得到 状态转移方程:

放物品 i :dp[i][j] = dp[i - 1][j - v[i]] + w[i];

不放物品 i :dp[i][j] = dp[i - 1][j]。

直观方法:

// v和w数组长度都是 N + 1,v[0]和w[0]都是0

private static void backpack1(int N, int V, int[] v, int[] w) {

int[][] dp = new int[N + 1][V + 1];

for (int i = 1; i <= N; ++i) {

for (int j = 1; j <= V; ++j) {

dp[i][j] = dp[i - 1][j];

if (j >= v[i]) {

dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - v[i]] + w[i]);

}

}

}

System.out.println(dp[N][V]);

}

这种方法空间不是最优的。观察代码发现,dp[i]只跟dp[i-1]有关,所以可以将二维降成一维。

优化方法:

private static void backpack2(int N, int V, int[] v, int[] w) {

int[] dp = new int[V + 1];

for (int i = 1; i <= N; ++i) {

for (int j = V; j >= v[i]; --j) {

dp[j] = Math.max(dp[j], dp[j - v[i]] + w[i]);

}

}

System.out.println(dp[V]);

}

注意:

内层循环不能顺序枚举。dp[j - v[i]]实际上相当于 dp[i - 1][j - v[i]],而不是dp[i][j - v[i]],如果顺序枚举, dp[i] 的 j – v[i] 的位置已经被计算过,覆盖了。所以应该通过倒序枚举来规避这个问题。

两个要点:

若 dp[] 全部初始化为0,计算结果的 dp[V] 就是答案;

若 dp[0] 初始化为0,其它元素全部初始化为负无穷,则最后遍历dp[]得到最大值为答案。

解释如下:

dp[V] 一定是最大值。同样遍历了所有物品情况下,容量 V 大于 V – X ,最后得到的价值 dp[V] 必然大于 dp[V – X]。

dp数组初始化值全为 0 ,则允许dp[V]从任何一个初始项转化而来,并不一定是 dp[0]。最终结果如果从 dp[k] 转化而来,说明有 k 体积的空余。但是,如果我们更改一下dp数组初始化的情况:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值