背包问题-01背包问题

n个物品,总体积是V,每个物品的体积的vi,每个物品的最大价值是wi,在不超过V的体积下求最大价值
eg
背包容积为 5
物品数量为 4
物品的体积分别为 {0, 1, 2, 3, 4}
物品的价值分别为 {0, 2, 4, 4, 5}
思路
定义一个二位数组 int [][] f = new int[n+1][V+1]f[i][j]就表示在1~i个物品中选取体积小于V的情况的最大价值。对于该题我们可以得到如下的一张表
在这里插入图片描述
背包问题用到的就是动态规划,对于某个物品我无非就是装与不装,有n个物品所以有一层循环。体积的情况有0-V,这又是一重循环。在对于第i个物品体积为j(0~V)的情况下我只是需要判断装了i是否会比原先的价值大即可。
f[i-1][j]代表的是不放i时价值的最大值
f[i][j-v[i]]+w[i]放了i后此时的价值
每次都将较大的存入f[i][j]
换成公式则是

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

最后我再遍历找到装入最后一个物品后各个重量的价值的最大值即可;

public class Main {
    static int V = 5;//背包容积为5
    static int n = 4;//物品数量为4
    static int[] v = {0, 1, 2, 3, 4};//每个物品的体积
    static int[] w = {0, 2, 4, 4, 5};//每个物品的价值
public static void main(String[] args) {
    int[][] f = new int[n+1][V + 1]; //F[i][j]表示考虑前i个物品总体是j的情况下总价值最大是多少;
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j <= V; j++) {
            f[i][j] = f[i - 1][j];
            if (j >= v[i])//装入背包的物品不能超过总体积
                f[i][j] = Math.max(f[i][j], f[i - 1][j - v[i]] + w[i]);
        }
    }
    int res = 0;
    for (int i = 0; i <= V; i++)
        res = Math.max(f[n][i], res);
    System.out.println(res);
  }
}

但是这样解题还是太过于笨重了,对于01背包问题我们一个压缩至一个一维数组来解决。f[j]就表示装至体积为j的最大W。为了达到这一个效果我们需要从后往前遍历(保证在体积小于v的时候的是还不曾装过j物品的最大价值),我们还可以哦通过数学推出f[V]一定就是最小价值。于是我们可以的到如下代码。

public class Main {
    static int V = 5;//背包容积为5
    static int n = 4;//物品数量为4
    static int[] v = {0, 4, 2, 3, 4};//每个物品的体积
    static int[] w = {0, 2, 4, 4, 5};//每个物品的价值

    public static void main(String[] args) {
        int[] f = new int[V + 1];
        for (int i = 1; i <= n; i++)
            for (int j = V; j >= v[i]; j--)//终止条件可以写在这里
                f[j] = Math.max(f[j], f[j - v[i]] + w[i]);
        System.out.println(f[V]);
    }
}

01背包问题还有一个变种求体积恰好为V的时候的最大价值。这时候我们可以把f[0]初始化为0,其余初始化为int的最小值即可。这样就可以给保证f[V]一定是从0开始转化的(毕竟其他数在加这个数的价值很小很小)代码仅添加如下一行即可

Arrays.fill(f,Integer.MIN_VALUE);     f[0]=0;