0-1背包

题目分析

  • 假设dp(i,j)是最大承重为j、有前i件物品可选时的最大总价值,i∈[1,n],j∈[1,W]
    • dp(i,0)、dp(0,j)初始值均为0
    • 如果j< weights[i-1],那么dp(i,j)=dp(i-1,j)
    • 如果j≥ weights[i-1],那么dp(i,j)=max{dp(i-1,j),dp(i-1,j- weights[i-1])+ values[i-1]}

dp(3,7)就是当 capacity为7,取前3件物品的最大总价值

j≥最后一件物品时是有选择权的,当j<最后一件物品的重量,那么一定不能选

Solution - 二维数组

int maxValue1(int[] values, int[] weights, int capacity) {
    if (values == null || values.length == 0) return 0;
    if (weights == null || weights.length == 0) return 0;
    if (values.length != weights.length || capacity <= 0) return 0;
    int[][] dp = new int[values.length + 1][capacity + 1];
    for (int i = 1; i <= values.length; i++) {
        for (int j = 1; j <= capacity; j++) {
            if (j < weights[i - 1]) {
                dp[i][j] = dp[i - 1][j];
            } else {
                dp[i][j] = Math.max(
                    dp[i - 1][j], 
                    values[i - 1] + dp[i - 1][j - weights[i - 1]]);
            }
        }
    }
    return dp[values.length][capacity];
}

Solution - 一维数组

  • dp(i,j)都是由dp(i-1,k)推导出来的,也就是说,第i行的数据是由它的上一行第i-1行推导出来的
    因此,可以使用一维数组来优化

  • 当出现最大承重=物品重量,是最后一次有选择, 当小于物品重量,必然是不能选的,就是上一行的

int maxValue(int[] values, int[] weights, int capacity) {
    if (values == null || values.length == 0) return 0;
    if (weights == null || weights.length == 0) return 0;
    if (values.length != weights.length || capacity <= 0) return 0;
    int[] dp = new int[capacity + 1];
    for (int i = 1; i <= values.length; i++) {
        for (int j = capacity; j >= weights[i - 1]; j--) {
            dp[j] = Math.max(dp[j], values[i - 1] + dp[j - weights[i - 1]]);
        }
    }
    return dp[capacity];
}

变式题 - 恰好装满

■有n件物品和一个最大承重为W的背包,每件物品的重量是w、价值是v
口在保证总重量恰好等于W的前提下,选择某些物品装入背包,背包的最大总价值是多少?
口注意:每个物品只有1件,也就是每个物品只能选择0件或者1件
■dp(i,j)初始状态调整
口dp(i,0)=0,总重量恰好为0,最大总价值必然也为0
口dp(0,j)=-∞(负无穷),j≥1,负数在这里代表无法恰好装满

对于dp(1,9),如果选了(6,2)最大价值就是6+上一行7位置,如果不是负无穷,如能会出现正数,但是只要是凑不够就是不合理的

在这里插入图片描述

Solution

int maxValueExactly(int[] values, int[] weights, int capacity) {
    if (values == null || values.length == 0) return 0;
    if (weights == null || weights.length == 0) return 0;
    if (values.length != weights.length || capacity <= 0) return 0;
    int[] dp = new int[capacity + 1];
    for (int j = 1; j <= capacity; j++) {
        dp[j] = Integer.MIN_VALUE;
    }
    for (int i = 1; i <= values.length; i++) {
        for (int j = capacity; j >= weights[i - 1]; j--) {
            dp[j] = Math.max(dp[j], values[i - 1] + dp[j - weights[i - 1]]);
        }
    }
    return dp[capacity] < 0 ? -1 : dp[capacity];
}

Reference:小码哥MJ

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值