代码随想录算法训练营第四十六天 | LeetCode 139. 单词拆分、多重背包、背包总结

简介: 代码随想录算法训练营第四十六天 | LeetCode 139. 单词拆分、多重背包、背包总结

代码随想录算法训练营第四十六天 | LeetCode 139. 单词拆分、多重背包、背包总结

文章链接:单词拆分多重背包背包总结

视频链接:单词拆分

1. LeetCode 139. 单词拆分

1.1 思路

  1. 本题的那些单词就是物品,字符串就是背包,问用这些物品能否装满这个背包,每个物品能使用多次,因此是完全背包
  2. dp 数组及其下标含义:dp[i] 长度为 i 的字符串能被所给的单词组成则 dp[i] 为 true。因此最后 return 的是 dp[s.length()]
  3. 递推公式:假设在位置 i 到这个位置的前面的位置 j,如果这一段是字典里的单词,并且 dp[j]=true 那么 dp[i] 就为 true,因为需要保证这个单词前面的单词已经是在字符串 s 中,避免出现前面的单词都不对,但最后一个对了的情况
  4. dp 数组的初始化:dp[0]=true,根据递推公式,dp[0] 一定为 true,不然后面就无法递推,由于题目给的描述里没有字符串长度为 0 的情况,因此这个初始化就是为了递推公式服务的,非 0 下标就全为 false,因为不知道这些位置能否被字典里的单词组成
  5. 遍历顺序:在纯完全背包中两层 for 循环是可以颠倒的,如果求装满背包有多少种方法就要区分组合数还是排列数,求组合数是先物品再背包,求排列数是先背包再物品。本题是排列数,因为 applepenapple 和 penappleapple 是不一样的两个单词字符串。因此是先背包再物品,因为背包是非空的,所以 i 从 1 开始 for(int i=1;i<=s.length();i++)for(String word:wordDict)if(i>=word.length()&&dp[i-word.length()]&&word.equals(s.substring(i-word.length(),i))dp[i]=true。最后一个条件就是看单词是否在字符串的这一段子串中,倒数第二个条件就是上面的 dp[j]
  6. 打印 dp 数组:用于 debug

1.2 代码

// 另一种思路的背包算法
class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        boolean[] dp = new boolean[s.length() + 1];
        dp[0] = true;
        for (int i = 1; i <= s.length(); i++) {
            for (String word : wordDict) {
                int len = word.length();
                if (i >= len && dp[i - len] && word.equals(s.substring(i - len, i))) {
                    dp[i] = true;
                    break;
                }
            }
        }
        return dp[s.length()];
    }
}

2. 多重背包

2.1 介绍

有N种物品和一个容量为V的背包。第i种物品最多有Mi件可用,每件耗费的空间是Ci,价值是Wi。求解将哪些物品装入背包可使这些物品的耗费的空间总和不超过背包容量,且价值总和最大。

多重背包和01背包是非常像的, 为什么和01背包像呢?

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

例如:

背包最大重量为10。

物品为:

这两种情况是不是一样呢?因此就转换成01背包了,且每个物品只用一次

2.2 代码

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

3. 背包总结

3.1 简介

背包问题是动态规划里的非常重要的一部分,单独总结一下。以下是几种常见的背包:

在背包问题中,我们都是按照如下五部来逐步分析。

  1. 确定dp数组(dp table)以及下标的含义
  2. 确定递推公式
  3. dp数组如何初始化
  4. 确定遍历顺序
  5. 举例推导dp数组

其实这五部里哪一步都很关键,但确定递推公式确定遍历顺序都具有规律性和代表性,所以下面我从这两点来对背包问题做一做总结。

3.2 背包递推公式

问能否能装满背包(或者最多装多少):dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]); ,对应题目如下:

416.分割等和子集

1049.最后一块石头的重量 II

问装满背包有几种方法:dp[j] += dp[j - nums[i]] ,对应题目如下:

494.目标和

518. 零钱兑换 II

377.组合总和Ⅳ

70. 爬楼梯进阶版(完全背包)

问背包装满最大价值:dp[j] = max(dp[j], dp[j - weight[i]] + value[i]); ,对应题目如下:

474.一和零

问装满背包所有物品的最小个数:dp[j] = min(dp[j - coins[i]] + 1, dp[j]); ,对应题目如下:

322.零钱兑换

279.完全平方数

3.3 遍历顺序

01背包

01背包二维数组中我们讲解二维dp数组01背包先遍历物品还是先遍历背包都是可以的,且第二层for循环是从小到大遍历。

和在01背包一维数组中,我们讲解一维dp数组01背包只能先遍历物品再遍历背包容量,且第二层for循环是从大到小遍历。

完全背包

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

纯完全背包中,讲解了纯完全背包的一维dp数组实现,先遍历物品还是先遍历背包都是可以的,且第二层for循环是从小到大遍历。

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

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

相关题目如下:

组合数518. 零钱兑换 II

排列数377.组合总和Ⅳ70. 爬楼梯进阶版(完全背包)

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

求最小数322.零钱兑换279.完全平方数

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

相关文章
|
5月前
|
机器学习/深度学习 算法 机器人
【水下图像增强融合算法】基于融合的水下图像与视频增强研究(Matlab代码实现)
【水下图像增强融合算法】基于融合的水下图像与视频增强研究(Matlab代码实现)
497 0
|
5月前
|
机器学习/深度学习 算法 机器人
使用哈里斯角Harris和SIFT算法来实现局部特征匹配(Matlab代码实现)
使用哈里斯角Harris和SIFT算法来实现局部特征匹配(Matlab代码实现)
256 8
|
5月前
|
机器学习/深度学习 算法 自动驾驶
基于导向滤波的暗通道去雾算法在灰度与彩色图像可见度复原中的研究(Matlab代码实现)
基于导向滤波的暗通道去雾算法在灰度与彩色图像可见度复原中的研究(Matlab代码实现)
291 8
|
6月前
|
机器学习/深度学习 传感器 算法
【无人车路径跟踪】基于神经网络的数据驱动迭代学习控制(ILC)算法,用于具有未知模型和重复任务的非线性单输入单输出(SISO)离散时间系统的无人车的路径跟踪(Matlab代码实现)
【无人车路径跟踪】基于神经网络的数据驱动迭代学习控制(ILC)算法,用于具有未知模型和重复任务的非线性单输入单输出(SISO)离散时间系统的无人车的路径跟踪(Matlab代码实现)
403 2
|
5月前
|
机器学习/深度学习 数据采集 负载均衡
结合多种启发式解码方法的混合多目标进化算法,用于解决带工人约束的混合流水车间调度问题(Matlab代码实现)
结合多种启发式解码方法的混合多目标进化算法,用于解决带工人约束的混合流水车间调度问题(Matlab代码实现)
264 0
|
5月前
|
机器学习/深度学习 人工智能 算法
【基于TTNRBO优化DBN回归预测】基于瞬态三角牛顿-拉夫逊优化算法(TTNRBO)优化深度信念网络(DBN)数据回归预测研究(Matlab代码实现)
【基于TTNRBO优化DBN回归预测】基于瞬态三角牛顿-拉夫逊优化算法(TTNRBO)优化深度信念网络(DBN)数据回归预测研究(Matlab代码实现)
235 0
|
5月前
|
存储 人工智能 算法
从零掌握贪心算法Java版:LeetCode 10题实战解析(上)
在算法世界里,有一种思想如同生活中的"见好就收"——每次做出当前看来最优的选择,寄希望于通过局部最优达成全局最优。这种思想就是贪心算法,它以其简洁高效的特点,成为解决最优问题的利器。今天我们就来系统学习贪心算法的核心思想,并通过10道LeetCode经典题目实战演练,带你掌握这种"步步为营"的解题思维。
|
Unix Shell Linux
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
本文提供了几个Linux shell脚本编程问题的解决方案,包括转置文件内容、统计词频、验证有效电话号码和提取文件的第十行,每个问题都给出了至少一种实现方法。
350 6
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
|
Python
【Leetcode刷题Python】剑指 Offer 32 - III. 从上到下打印二叉树 III
本文介绍了两种Python实现方法,用于按照之字形顺序打印二叉树的层次遍历结果,实现了在奇数层正序、偶数层反序打印节点的功能。
204 6
|
搜索推荐 索引 Python
【Leetcode刷题Python】牛客. 数组中未出现的最小正整数
本文介绍了牛客网题目"数组中未出现的最小正整数"的解法,提供了一种满足O(n)时间复杂度和O(1)空间复杂度要求的原地排序算法,并给出了Python实现代码。
449 2

热门文章

最新文章