当前位置:   article > 正文

代码随想录算法训练营day51 || 139. 单词拆分,多重背包问题

代码随想录算法训练营day51 || 139. 单词拆分,多重背包问题

讲解链接:

   动态规划之完全背包,你的背包如何装满?| LeetCode:139.单词拆分_哔哩哔哩_bilibili

   代码随想录

背包问题总结

   代码随想录

139. 单词拆分

思路:首先我们摒弃最近在练习的是动态规划题目的思想,就看这道题目,给了一个数组,给出一个参考量;这个参考量是使用数组中的内容进行参考量本身的判断操作,此时再拾起背包问题的思想,我们可以得到背包问题不仅仅是直接在题目中说明本体是用物品填充背包,其形式就是给一个数组去构成或者填充某个参考量。

本题是使用单词去构成一个字符串,且数组内的单词可以被多次使用,直至装满整个字符串,题目类型是完全背包问题。本题的递推公式是

if([j, i] 这个区间的子串出现在字典里 && dp[j]是true) 那么 dp[i] = true

没有想到,暂时先记忆吧。另外遍历顺序上面,是采用排列的方式进行遍历,即先遍历重量,再物品,因为给出的测试用例的例子中apple这个内容出现在了s的开头和结尾;如果仍然采用组合遍历,那么s的任何子串与单词进行判断时,只是使用[0,i]范围的单词,如果s的开头部分的子串在数组的后面,那么dp[j]就会被赋值为false,但好不容易走到后部分数组后,虽然前部分的匹配是可以满足了,但是s的中间部分以及后部分已经错过了。因为那部分遍历的时候都是建立在前部分为true的情况上,才可以为true;而前部分所对应的单词在数组后部分,所以dp[j]会一直是false,导致中间部分和后面部分的遍历也是false。

所以一定的每个位置都需要使用使用的单词去匹配。另外,s在匹配时,需要一个一个位置的增加字符来扩大遍历范围。并且每个位置的增加后,都需要与所有的单词进行一下比较。不可以0,i位置与某个单词吻合,我接下来就从i+1位置开始遍历了,不行,这忽略单词之间是可能存在交集的,从而本来dp[j],dp[j+1]都是true,而没有在增加一个位置后仍然对所有单词进行遍历,会造成dp[j]=true, dp[j+1]=false。下面的代码就是我所描述的出错情况对应的代码。

  1. int index = 0;
  2. for(int i=1; i<s.length(); i++){
  3. if(wordDict.contains(s.subString(index, i) && dp[index]){
  4. dp[i] = true;
  5. index = i+1;
  6. }
  7. }

这是正确的解法。 

  1. // 时间复杂度O(n^2)
  2. // 空间复杂度O(n)
  3. class Solution {
  4. public boolean wordBreak(String s, List<String> wordDict) {
  5. HashSet<String> set = new HashSet<>(wordDict);
  6. boolean[] valid = new boolean[s.length() + 1];
  7. valid[0] = true;
  8. for (int i = 1; i <= s.length(); i++) {
  9. // 如果某一个位置已经可以被表示,那么这个位置不用再去进行考虑,避免true会被额外的覆盖掉,反而会导致出错
  10. for (int j = 0; j < i && !valid[i]; j++) {
  11. if (set.contains(s.substring(j, i)) && valid[j]) {
  12. valid[i] = true;
  13. }
  14. }
  15. }
  16. return valid[s.length()];
  17. }
  18. }

多重背包问题

思路:将有限各物品转换为多个物品的01背包问题即可求解。另外优化的思路是增加一个for循环,在重量中去控制某类相同物品的使用个数,从而减少因weights和values扩容而带来的额外开销。

另外关于01背包滚动数组和二维数组的递推公式

dp[j] = Math.max(dp[j], dp[j-weights[i]]+values[i]);

dp[i][j]= Math.max(dp[i-1][j], dp[i-1][j-weights[i]]+values[i]);

滚动数组的递推重量最佳的是从大重量往小重量遍历,防止重复赋值的情况;重复赋值是用来求解完全背包问题的;

二维数组的赋值,dp[i-1][j-weights[i]]+values[i]这项一定也是dp[i-1][?],不是dp[i][?],一方面的理由是i一定是参考i-1之前的内容,放入i这个物品,所体现的也是增加weights,而不是直接上升到dp[i],另外dp[i][j-weights[i]]+values[i]相当于在不断的将当前物品累加上i-1之前物品的使用情况上,这肯定是不对的。

  1. // 时间复杂度O(m*n*k)
  2. // 空间复杂度O(n)
  3. import java.util.*;
  4. public class Main {
  5. public static void main(String[] args) {
  6. // 多重背包问题,解法是转换成01背包问题
  7. Scanner scanner = new Scanner(System.in);
  8. // 重量
  9. int C = scanner.nextInt();
  10. int N = scanner.nextInt();
  11. int[] weights = new int[N];
  12. int[] values = new int[N];
  13. int[] nums = new int[N];
  14. for(int i=0; i<N; i++){
  15. weights[i] = scanner.nextInt();
  16. }
  17. for(int i=0; i<N; i++){
  18. values[i] = scanner.nextInt();
  19. }
  20. for(int i=0; i<N; i++){
  21. nums[i] = scanner.nextInt();
  22. }
  23. // 使用滚动数组进行01背包问题的求解
  24. int[] dp = new int[C+1];
  25. // 初始化dp[0]赋值为零
  26. // 组合方式遍历即可,不强调放入物品的先后顺序
  27. for(int i=0; i<N; i++){
  28. for(int j=C; j>0; j--){
  29. for(int k=1; k<=nums[i] && j>=weights[i]*k; k++)
  30. dp[j] = Math.max(dp[j], dp[j-weights[i]*k]+values[i]*k);
  31. }
  32. }
  33. System.out.println(dp[C]);
  34. }
  35. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/214799
推荐阅读
相关标签
  

闽ICP备14008679号