0-1背包问题及其空间优化概述

有一个容量为 N 的背包,要用这个背包装下物品的价值最大,这些物品有两个属性:体积 w 和价值 v。

定义一个二维数组 dp 存储最大价值,其中 dp[i][j] 表示前 i 件物品体积不超过 j 的情况下能达到的最大价值。设第 i 件物品体积为 w,价值为 v,根据第 i 件物品是否添加到背包中,可以分两种情况讨论:

第 i 件物品没添加到背包,总体积不超过 j 的前 i 件物品的最大价值就是总体积不超过 j 的前 i-1 件物品的最大价值,dp[i][j] = dp[i-1][j]
第 i 件物品添加到背包中,dp[i][j] = dp[i-1][j-w] + v
第 i 件物品可添加也可以不添加,取决于哪种情况下最大价值更大。因此,0-1 背包的状态转移方程为:
dp[i][j] = max(dp[i-1][j], dp[i-1][j-w] + v)

// W 为背包总体积
// N 为物品数量
// weights 数组存储 N 个物品的重量
// values 数组存储 N 个物品的价值
public int knapsack(int W, int N, int[] weights, int[] values) {
    int[][] dp = new int[N + 1][W + 1];
    for (int i = 1; i <= N; i++) {
        int w = weights[i - 1], v = values[i - 1];
        for (int j = 1; j <= W; j++) {
            if (j >= w) {
                dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - w] + v);
            } else {
                dp[i][j] = dp[i - 1][j];
            }
        }
    }
    return dp[N][W];
}

空间优化:
观察方程:dp[i][j] = max(dp[i-1][j], dp[i-1][j-w] + v)

  1. 上述方法是用的二维dp数组存储的子问题解。假设i为数组的行数,j为数组的列数。那么由方程易知第i行的子问题解只与第i-1行子问题解有关系。或者说当前行的dp[i][x] 0 <= x <= W,由前一行的子问题解推出dp[i-1][y] 0 <= y <= W
  2. 那么如果能够用当前行的dp数据覆盖前一行的dp数据,就可以只用一维数组存储子问题的解。
  3. 若可以只用一维数组存储,外层循环i还是从1到N,但是方程变为:dp[j] = max(dp[j], dp[j-w] + v)
  4. 下面分析当i==a时 (1 <= a <= N),如果j从小到大遍历,会导致计算dp[j]出错,因为dp[j]是由i ==a-1时算出来的dp[j]和dp[j-w]推出来的。而从小到大遍历会提前改变不该改变的值,反之从大到小遍历就不会,每次覆盖的值都是此层循环之后用不到的值。画图会更好理解。
public int knapsack(int W, int N, int[] weights, int[] values) {
    int[] dp = new int[W + 1];
    for (int i = 1; i <= N; i++) {
        int w = weights[i - 1], v = values[i - 1];
        for (int j = W; j >= 1; j--) {
            if (j >= w) {
                dp[j] = Math.max(dp[j], dp[j - w] + v);
            }
        }
    }
    return dp[W];
}

再简化一下代码

public int knapsack(int W, int N, int[] weights, int[] values) {
    int[] dp = new int[W + 1];
    for (int i = 1; i <= N; ++i)
    	for (int j = weights[i-1]; j >= 1; --j)
    		if (dp[j] < dp[j-weights[i-1]] + values[i-1])
    			dp[j] = dp[j-weights[i-1]] + values[i-1];
    return dp[W];
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值