当前位置:   article > 正文

代码随想录算法训练营 Day 46 | 139.单词拆分,关于多重背包,你该了解这些!,背包问题总结篇!

代码随想录算法训练营 Day 46 | 139.单词拆分,关于多重背包,你该了解这些!,背包问题总结篇!

139.单词拆分

讲解链接:代码随想录-139.单词拆分

  1. 确定 dp 数组以及下标的含义:dp[i] : 字符串长度为 i 的话,dp[i]为 true,表示可以拆分为一个或多个在字典中出现的单词。

  2. 确定递推公式:如果确定 dp[j] 是 true,且 [j, i] 这个区间的子串出现在字典里,那么 dp[i]一定是 true。(j < i )。
    所以递推公式是 if([j, i] 这个区间的子串出现在字典里 && dp[j]是 true) 那么 dp[i] = true。

  3. dp 数组如何初始化:从递推公式中可以看出,dp[i] 的状态依靠 dp[j]是否为 true,那么 dp[0]就是递推的根基,dp[0]一定要为 true,否则递推下去后面都都是 false 了。

  4. 确定遍历顺序:题目中说是拆分为一个或多个在字典中出现的单词,所以这是完全背包。还要讨论两层 for 循环的前后顺序。

    1. 如果求组合数就是外层 for 循环遍历物品,内层 for 遍历背包。
    2. 如果求排列数就是外层 for 遍历背包,内层 for 循环遍历物品。
    3. 而本题其实我们求的是排列数,为什么呢。 拿 s = "applepenapple", wordDict = ["apple", "pen"] 举例。
      "apple", "pen" 是物品,那么我们要求 物品的组合一定是 "apple" + "pen" + "apple" 才能组成 "applepenapple"。
      "apple" + "apple" + "pen" 或者 "pen" + "apple" + "apple" 是不可以的,那么我们就是强调物品之间顺序。
      所以说,本题一定是 先遍历 背包,再遍历物品。
  1. public boolean wordBreak(String s, List<String> wordDict) {
  2. boolean[] dp = new boolean[s.length() + 1];
  3. dp[0] = true;
  4. for (int i = 1; i < dp.length; i++) {
  5. for (int j = 0; j < wordDict.size(); j++) {
  6. String str = wordDict.get(j);
  7. Integer len = str.length();
  8. if (i >= len && dp[i - len] && str.equals(s.substring(i - len, i))) {
  9. dp[i] = true;
  10. break;
  11. }
  12. }
  13. }
  14. return dp[dp.length - 1];
  15. }

关于多重背包,你该了解这些!

讲解链接:代码随想录-关于多重背包,你该了解这些! 

版本二:改变遍历个数

  1. public void testMultiPack2() {
  2. // 版本二:改变遍历个数
  3. int[] weight = new int[]{1, 3, 4};
  4. int[] value = new int[]{15, 20, 30};
  5. int[] nums = new int[]{2, 3, 2};
  6. int bagWeight = 10;
  7. int[] dp = new int[bagWeight + 1];
  8. for (int i = 0; i < weight.length; i++) { // 遍历物品
  9. for (int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量
  10. // 以上为01背包,然后加一个遍历个数
  11. for (int k = 1; k <= nums[i] && (j - k * weight[i]) >= 0; k++) { // 遍历个数
  12. dp[j] = Math.max(dp[j], dp[j - k * weight[i]] + k * value[i]);
  13. }
  14. System.out.println(Arrays.toString(dp));
  15. }
  16. }
  17. }

版本一:改变物品数量为 01 背包格式

  1. public void testMultiPack1() {
  2. // 版本一:改变物品数量为01背包格式
  3. List<Integer> weight = new ArrayList<>(Arrays.asList(1, 3, 4));
  4. List<Integer> value = new ArrayList<>(Arrays.asList(15, 20, 30));
  5. List<Integer> nums = new ArrayList<>(Arrays.asList(2, 3, 2));
  6. int bagWeight = 10;
  7. for (int i = 0; i < nums.size(); i++) {
  8. while (nums.get(i) > 1) { // 把物品展开为i
  9. weight.add(weight.get(i));
  10. value.add(value.get(i));
  11. nums.set(i, nums.get(i) - 1);
  12. }
  13. }
  14. int[] dp = new int[bagWeight + 1];
  15. for (int i = 0; i < weight.size(); i++) { // 遍历物品
  16. for (int j = bagWeight; j >= weight.get(i); j--) { // 遍历背包容量
  17. dp[j] = Math.max(dp[j], dp[j - weight.get(i)] + value.get(i));
  18. }
  19. System.out.println(Arrays.toString(dp));
  20. }
  21. }

背包问题总结篇!

讲解链接:代码随想录-背包问题总结篇!

01 背包

动态规划:关于 01 背包问题,你该了解这些! 中我们讲解二维 dp 数组 01 背包先遍历物品还是先遍历背包都是可以的,且第二层 for 循环是从小到大遍历。

动态规划:关于 01 背包问题,你该了解这些!(滚动数组) 中,我们讲解一维 dp 数组 01 背包只能先遍历物品再遍历背包容量,且第二层 for 循环是从大到小遍历。

一维 dp 数组的背包在遍历顺序上和二维 dp 数组实现的 01 背包其实是有很大差异的,大家需要注意!

完全背包

说完 01 背包,再看看完全背包。

动态规划:关于完全背包,你该了解这些! (opens new window)中,讲解了纯完全背包的一维 dp 数组实现,先遍历物品还是先遍历背包都是可以的,且第二层 for 循环是从小到大遍历。

但是仅仅是纯完全背包的遍历顺序是这样的,题目稍有变化,两个 for 循环的先后顺序就不一样了。

如果求组合数就是外层 for 循环遍历物品,内层 for 遍历背包。

如果求排列数就是外层 for 遍历背包,内层 for 循环遍历物品。

相关题目如下:

如果求最小数,那么两层 for 循环的先后顺序就无所谓了,相关题目如下:

对于背包问题,其实递推公式算是容易的,难是难在遍历顺序上,如果把遍历顺序搞透,才算是真正理解了。

总结

​​

 

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号