【力扣一刷】代码随想录day46(动态规划part8 - 背包问题专题:139.单词拆分、多重背包基础、56. 携带矿石资源(第八期模拟笔试)、背包问题总结篇)

目录

【139.单词拆分】

方法一  暴力回溯​编辑

方法二  完全背包做法(很抽象,不如回溯思路清晰)

【多重背包基础】

【56. 携带矿石资源(第八期模拟笔试)】

【背包问题总结篇】(参考代码随想录-背包问题总结)

1、背包问题判断

2、背包递推公式

3、求排列数 or 组合数


【139.单词拆分】

方法一  暴力回溯

关键:

1、如果子节点不包含在单词字典中,直接跳过,不遍历该子节点。

2、如果遍历过程中,发现以start开头的所有子节点都返回false,则标记start位置开头的字符串都不能匹配,即mem[start] = -1,后序再遍历到start开头,直接返回false,不再进入for循环。(通过的关键)

3、如果有一个分支能到达最后,则一路往上返回true。

注意:

这道题不需要返回拆分的路径,因此也不需要记录路径和恢复现场。

class Solution {
    Set<String> set;
    int[] mem;
    public boolean wordBreak(String s, List<String> wordDict) {
        set = new HashSet<>(wordDict);  // 将list转换为set,加快contains查找速度
        mem = new int[s.length()];  // 记录以start~s.length()都不能匹配的情况
        return backtracking(s, 0); // 回溯
    }

    public boolean backtracking(String s, int start){
        // 1、递归结束条件
        if (start == s.length()) return true;
        if (mem[start] == -1) return false; // 之前的搜索已经发现从start开始后面的字符串无法匹配
        // 2、单层递归逻辑-遍历子节点
        for (int i = start + 1; i <= s.length(); i++){ // i表示结束位置索引,subString左闭右开
            String subStr = s.substring(start, i);
            if (set.contains(subStr)) {
                if (backtracking(s, i)) return true;
            }
        }
        mem[start] = -1; // 标记以start开始后面的字符串都没办法匹配
        return false;
    }
}
  • 时间复杂度:O(2^n),n是字符串长度,因为每一个字符都有两个状态,切割和不切割
  • 空间复杂度:O(n),算法递归系统调用栈的空间,最长和字符串长度相等

方法二  完全背包做法(很抽象,不如回溯思路清晰)

分析:

1、单词(物品)可无限次取,属于完全背包问题,正序遍历背包容量。

2、s中单词的顺序固定,但是不一定是按wordDict的顺序排,此处对应的是排列,外层for遍历背包容量。

3、初始化,默认初始化除dp[0]外的所有值为false,dp[0]=true,后面才有可能为true,否则全都是false。

4、dp[i]的含义,i 代表背包的容量,i 前面的子串能否能够被字典中的单词拼接出来,能设为true,否保持默认值false。

5、递推关系,j 代表的是指向s的字符索引,如果dp[j]为true,说明 j 前面的子串能够被拼接,从 j 开始到 i 前面的子串如果也包含在字典中,则 i 前面的字符串都能被拼接,dp[i] 设为true。

class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        Set<String> set = new HashSet<>(wordDict);
        boolean[] dp = new boolean[s.length() + 1]; // dp[0]对应空字符串,默认初始化所有值为false
        dp[0] = true; // dp[0]=true,后面才有可能为true,否则全都是false
        // s中单词的顺序固定,但是不一定是按wordDict的顺序,所以这里是排列数
        for (int i = 1; i < s.length() + 1; i++){  // 外层for遍历背包容量
            for (int j = 0; j < i; j++){
                if (dp[j] == true && set.contains(s.substring(j, i))){ // i=字符对应索引+1
                    dp[i] = true;
                }
            }
        }
        return dp[s.length()];
    }
}
  • 时间复杂度:O(n^3),两个for循环,加上s.substring(j, i)的复杂度与子串长度有关,最坏为n
  • 空间复杂度:O(n),n为字符串的长度

【多重背包基础】

多重背包和01背包的关系:

多重背包每件物品最多有Mi件可用,把Mi件摊开,其实就是一个01背包问题了。

多重背包:

转化为01背包:

【56. 携带矿石资源(第八期模拟笔试)】

关键:在最内层增加一层for遍历,将多重背包转化为01背包。

判断条件要加上j >= k*weights[i],只有容量足够容纳k个相同物品,才考虑放。

import java.util.*;
public class Main{
    public static void main (String[] args) {
        // 读取数据
        Scanner sc = new Scanner(System.in);
        int C = sc.nextInt();
        int N = sc.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] = sc.nextInt();
        for (int i = 0; i < N; i++) values[i] = sc.nextInt();
        for (int i = 0; i < N; i++) nums[i] = sc.nextInt();
        
        int[] dp = new int[C+1];
        for (int i = 0; i < N; i++){
            for (int j = C; j >= weights[i]; j--){
                // 在最内层增加一层for遍历,将多重背包转化为01背包
                for (int k = 1; k <= nums[i] && j >= k*weights[i]; k++){
                    dp[j] = Math.max(dp[j], dp[j - k * weights[i]] + k * values[i]);
                }
            }
        }
        System.out.println(dp[C]);
    }
}
  • 时间复杂度:O(N × C × k),N为物品种类个数,C背包容量,k单类物品数量
  • 空间复杂度:O(C),C为背包容量

【背包问题总结篇】(参考代码随想录-背包问题总结

1、背包问题判断

01背包问题:每种物品最多只能取1次

完全背包问题:每种物品可以取无限次

多重背包问题:每种物品可以取的次数有规定

2、背包递推公式

问背包装满最大价值:

dp[j] = max(dp[j], dp[j - weight[i]] + value[i]); ,对应题目如下:

问能否能装满背包(或者最多装多少):

dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]); ,对应题目如下:

问装满背包有几种方法:

dp[j] += dp[j - nums[i]] ,对应题目如下:

问装满背包所有物品的最小个数:

dp[j] = min(dp[j - coins[i]] + 1, dp[j]); ,对应题目如下:

3、求排列数 or 组合数

求组合数:结果按物品排序固定顺序来

外层for循环遍历物品,内层for遍历背包。

动态规划:518.零钱兑换II(opens new window)

求排列数:结果不按物品排序固定顺序来

外层for遍历背包,内层for循环遍历物品。

动态规划:377. 组合总和 Ⅳ (opens new window)动态规划:70. 爬楼梯进阶版(完全背包)

  • 27
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值