背包问题

假设有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]);
    }
 
原文地址:https://www.cnblogs.com/stAr-1/p/8575137.html