背包问题
假设有abcde5个物品,重量为w1...,价值为v1...,背包的承重量为c,怎么放的价值更高
一、0/1背包问题
5中物品只有一件,动态规划的子问题是前i个物品放在承重量为j的背包中,状态变量:dp[i][j]
遍历时i在外层,j在内层,也就是每次循环先求出前i-1个物品在不同载重量的情况下的自大价值
然后增加一个物品,也就是前i个物品。
而在求前i个物品的最大价值时根据前i-1的结果:
1.当前载重量<新增的第i件物品的重量w[i],也就是装不下
dp[i][j] = dp[i-1][j]
2.装得下
dp[i][j] = max(dp[i-1][j-w[i]]+v[i],dp[i-1][j]):比较装和不装,其中如果装当前物品,那么重量占了w[i],前边i-1件物品只能使用j-w[i]的重量
到这里时间复杂度没法优化,但是还能优化空间复杂度
我们可以观察到,这里的i只是记录进行到前多少件物品了,其实我们不需要前几件的结果,我们需要的是最后的结果,所以我们没有必要记录
i的信息。可以用一个一维数组dp[j]只记录当前信息i件物品下,不同载重量的价值。然后每次都更新。更新到最后就是想要的
这里虽然不记录i的信息,但是还是要保留循环,代表这是前i件的情况,i不同的情况下,dp[j]是不同的。
注意这里需要倒着遍历,因为dp[j]会不断更新,前边更新的结果不能影响后边
如果从前边开始,前边的dp[j]就会更新为i的状态,i-1的状态就消失了,但是后边需要这些
如果从后边开始,前边的用不到后边的结果
public void p01(int[] w,int[] v,int c){ int[] dp = new int[c+1]; for (int i = 0; i < w.length; i++) { // 注意这里需要倒着遍历,因为dp[j]会不断更新,前边更新的结果不能影响后边 // 如果从前边开始,前边的dp[j]就会更新为i的状态,i-1的状态就消失了,但是后边需要这些 // 如果从后边开始,前边的用不到后边的结果 for (int j = c; j >=w[i]; j--) { dp[j] = Math.max(dp[j],dp[j-w[i]]+v[i]); } } System.out.println(dp[c]); }
2.完全背包问题
物品数量没有限制
public void pComplete(int[] w,int[] v,int c){ int[] dp = new int[c+1]; for (int i = 0; i < w.length; i++) { //这里应该从前往后遍历,01背包不能从前往后的原因其实是如果当前承重量可以装下多件第i件物品时 //01问题不能装多个,所以装完后要用前一次的状态比较(前一次肯定是没有装i的) //但是完全背包不同,装完后要用最新的状态比较,因为还可以继续装i for (int j = w[j]; j <=c; j++) { dp[j] = Math.max(dp[j],dp[j-w[i]]+v[i]); } } System.out.println(dp[c]); }