代码随想录算法训练营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。下面的代码就是我所描述的出错情况对应的代码。

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

这是正确的解法。 

// 时间复杂度O(n^2)
// 空间复杂度O(n)

class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {

        HashSet<String> set = new HashSet<>(wordDict);
        boolean[] valid = new boolean[s.length() + 1];
        valid[0] = true;

        for (int i = 1; i <= s.length(); i++) {
            // 如果某一个位置已经可以被表示,那么这个位置不用再去进行考虑,避免true会被额外的覆盖掉,反而会导致出错
            for (int j = 0; j < i && !valid[i]; j++) {
                if (set.contains(s.substring(j, i)) && valid[j]) {
                    valid[i] = true;
                }
            }
        }

        return valid[s.length()];
    }
}

多重背包问题

思路:将有限各物品转换为多个物品的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之前物品的使用情况上,这肯定是不对的。

// 时间复杂度O(m*n*k)
// 空间复杂度O(n)

import java.util.*;

public class Main {

    public static void main(String[] args) {
       // 多重背包问题,解法是转换成01背包问题
        Scanner scanner = new Scanner(System.in);
        // 重量
        int C = scanner.nextInt();
        int N = scanner.nextInt();
        int[] weights = new int[N];
        int[] values = new int[N];
        int[] nums = new int[N];
        for(int i=0; i<N; i++){
            weights[i] = scanner.nextInt();
        }
        for(int i=0; i<N; i++){
            values[i] = scanner.nextInt();
        }
        for(int i=0; i<N; i++){
            nums[i] = scanner.nextInt();
        }

        // 使用滚动数组进行01背包问题的求解
        int[] dp = new int[C+1];
        // 初始化dp[0]赋值为零
        // 组合方式遍历即可,不强调放入物品的先后顺序
        for(int i=0; i<N; i++){
            for(int j=C; j>0; j--){
                for(int k=1; k<=nums[i] && j>=weights[i]*k; k++)
                        dp[j] = Math.max(dp[j], dp[j-weights[i]*k]+values[i]*k);
            }
        }

        System.out.println(dp[C]);
    }


}

  • 34
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
代码随想录算法训练营是一个优质的学习和讨论平台,提供了丰富的算法训练内容和讨论交流机会。在训练营中,学员们可以通过观看视频讲解来学习算法知识,并根据讲解内容进行刷题练习。此外,训练营还提供了刷题建议,例如先看视频、了解自己所使用的编程语言、使用日志等方法来提高刷题效果和语言掌握程度。 训练营中的讨论内容非常丰富,涵盖了各种算法知识点和解题方法。例如,在第14天的训练营中,讲解了二叉树的理论基础、递归遍历、迭代遍历和统一遍历的内容。此外,在讨论中还分享了相关的博客文章和配图,帮助学员更好地理解和掌握二叉树的遍历方法。 训练营还提供了每日的讨论知识点,例如在第15天的讨论中,介绍了层序遍历的方法和使用队列来模拟一层一层遍历的效果。在第16天的讨论中,重点讨论了如何进行调试(debug)的方法,认为掌握调试技巧可以帮助学员更好地解决问题和写出正确的算法代码。 总之,代码随想录算法训练营是一个提供优质学习和讨论环境的平台,可以帮助学员系统地学习算法知识,并提供了丰富的讨论内容和刷题建议来提高算法编程能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [代码随想录算法训练营每日精华](https://blog.csdn.net/weixin_38556197/article/details/128462133)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值