round1/day14&15/动规3-01背包

1. 关于动规

  1. 01背包思路
    1. 二维数组
    2. 一维数组
      • 创建dp数组:j是目标总价,dp[j]可以是int[],也可能是boolean[]。例:int[]的dp:是在0~i个物品中,可以实现的里目标总价最近的价值
      • 初始化:dp[j=0]?
      • 状态转移:
        • 注意遍历顺序:物品顺序遍历,然后背包容量倒序遍历,其遍历边界是到j-weight[i]为止
        • 比较2种情况Math.max():从i+1个物品遍历到第i个物品时:
          • 情况1:用得到物品i:dp[j-weight[i]] + val[i]
          • 情况2:用不到物品i:仍然是(在物品数组i+1时搭配出来的)dp[j]不变
          • 注意:如果返回int[],则这里的比较是指Math.max;如果返回boolean[],则这里的比较是指 v||
  2. 待施工

思路练习

  1. 怎么把思路转化成01背包?(谁是j,谁是dp[j]?谁是背包容量,谁是物品价值/重量?)
  2. 01背包的一维数组要注意什么?(j的遍历顺序?j的遍历边界?)

2. 例题

lc1049 最后一块石头重量II

前置知识点

int[]数组求和:Arrays.stream(nums).sum()

思路

转为01背包问题:

  1. 把石头分成尽可能等重的两堆,然后两堆相撞求得解
  2. 怎么样可以凑到尽可能=总重一半?用416分割等和子集的思想,即让i个石头中,凑到sum/2的可能性,只是这次不返回t/f,也不用追求完全=sum/2,而是要返回最佳情况下,凑到的最大石头总重
  3. 转嫁为背包思路
    • 谁是背包?什么是背包容量?——sum/2即为背包容量
      • 谁是j?——当背包容量为j时……这个j是滚动变化的,而不是静止的sum/2
    • 谁是物品?——石头
      • 谁是i?——石头们,0~i即为在前i个石头里,是否存在部分的石头可以使……
      • 谁是物品重量?——每个石头的重量stones[i],即为在状态转移比较更新时的dp[j-stones[i]]+stones[i]中的第一个stones[i],此处代表weight[i]
      • 谁是物品价值?——本题中,也是每个石头的重量stones[i],即为在状态转移比较更新时dp[j-stones[i]]+stones[i]中的第二个stones[i]

01背包的应用:

  1. 创建dp数组:故设定dp[j]表示,在背包容量(石头目标总重)为j时,能凑到的离背包容量最近的最大石头总重为dp[j]
  2. 状态转移:遍历到第i个石头时:
    • 情况1:需要用到这i个石头 dp[j-stones[i]]+stones[i]
    • 情况2:用不到这i个石头 dp[j]
    • 比较2情况并更新:dp[j]=max(dp[j],dp[j-stones[i]]+stones[i])
  3. 遍历顺序:一维数组,先顺序遍历物品(石头)i,再倒序遍历背包容量j
  4. 初始化:若j=0,则答案为0,跟默认的是一样的,相当于不用初始化

易错点

注意j遍历的边界,为了步数组越界,需要的是j-stones[i]>=0,即j>=stones[i]

代码实现

class Solution {
    public int lastStoneWeightII(int[] stones) {
        //计算目标重量:总重的一半
        int sum = Arrays.stream(stones).sum();
        int targetsum=sum/2;

        //建立dp数组,这里已经默认dp[?]=0了
        int[] dp=new int[targetsum+1];

        //初始化:本题没有

        //状态转移(注意一维数组的遍历顺序
        //易错,注意j遍历的边界,为了步数组越界,需要的是j-stones[i]>=0,即j>=stones[i]
        for(int i=0;i<stones.length;i++){
            for(int j=targetsum;j>=stones[i];j--){
                dp[j]=Math.max(dp[j],dp[j-stones[i]]+stones[i]);
            }
        }

        //最终撞击两堆石头,即sum-2dp[targetsum]
        return sum-2*dp[targetsum];
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值