代码随想录算法训练营第四十六天 | 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.完全平方数

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

相关文章
|
2月前
|
机器学习/深度学习 人工智能 自然语言处理
【自然语言处理】TF-IDF算法在人工智能方面的应用,附带代码
TF-IDF算法在人工智能领域,特别是自然语言处理(NLP)和信息检索中,被广泛用于特征提取和文本表示。以下是一个使用Python的scikit-learn库实现TF-IDF算法的简单示例,并展示如何将其应用于文本数据。
215 65
|
28天前
|
机器学习/深度学习 存储 算法
经典算法代码
这段代码展示了多个经典算法,包括:穷举法解决“百钱买百鸡”问题;递推法计算“猴子吃桃”问题;迭代法求解斐波那契数列及折纸高度超越珠峰的问题。同时,还提供了希尔排序算法实现及披萨票务订购系统和汉诺塔问题的链表存储解决方案。每部分通过具体案例解释了算法的应用场景与实现方法。
23 3
|
2月前
|
机器学习/深度学习 人工智能 算法
【人工智能】传统语音识别算法概述,应用场景,项目实践及案例分析,附带代码示例
传统语音识别算法是将语音信号转化为文本形式的技术,它主要基于模式识别理论和数学统计学方法。以下是传统语音识别算法的基本概述
56 2
|
2月前
|
搜索推荐 算法 Java
|
2月前
|
人工智能 算法 数据可视化
DBSCAN密度聚类算法(理论+图解+python代码)
DBSCAN密度聚类算法(理论+图解+python代码)
|
2月前
|
数据采集 搜索推荐 算法
【高手进阶】Java排序算法:从零到精通——揭秘冒泡、快速、归并排序的原理与实战应用,让你的代码效率飙升!
【8月更文挑战第21天】Java排序算法是编程基础的重要部分,在算法设计与分析及实际开发中不可或缺。本文介绍内部排序算法,包括简单的冒泡排序及其逐步优化至高效的快速排序和稳定的归并排序,并提供了每种算法的Java实现示例。此外,还探讨了排序算法在电子商务、搜索引擎和数据分析等领域的广泛应用,帮助读者更好地理解和应用这些算法。
25 0
|
2月前
|
搜索推荐 算法 Java
插入排序算法(Java代码实现)
这篇文章通过Java代码示例详细解释了插入排序算法的实现过程,包括算法的基本思想、核心代码、辅助函数以及测试结果,展示了如何通过插入排序对数组进行升序排列。
|
3天前
|
传感器 算法 C语言
基于无线传感器网络的节点分簇算法matlab仿真
该程序对传感器网络进行分簇,考虑节点能量状态、拓扑位置及孤立节点等因素。相较于LEACH算法,本程序评估网络持续时间、节点死亡趋势及能量消耗。使用MATLAB 2022a版本运行,展示了节点能量管理优化及网络生命周期延长的效果。通过簇头管理和数据融合,实现了能量高效和网络可扩展性。
|
1月前
|
算法 BI Serverless
基于鱼群算法的散热片形状优化matlab仿真
本研究利用浴盆曲线模拟空隙外形,并通过鱼群算法(FSA)优化浴盆曲线参数,以获得最佳孔隙度值及对应的R值。FSA通过模拟鱼群的聚群、避障和觅食行为,实现高效全局搜索。具体步骤包括初始化鱼群、计算适应度值、更新位置及判断终止条件。最终确定散热片的最佳形状参数。仿真结果显示该方法能显著提高优化效率。相关代码使用MATLAB 2022a实现。
|
1月前
|
算法 数据可视化
基于SSA奇异谱分析算法的时间序列趋势线提取matlab仿真
奇异谱分析(SSA)是一种基于奇异值分解(SVD)和轨迹矩阵的非线性、非参数时间序列分析方法,适用于提取趋势、周期性和噪声成分。本项目使用MATLAB 2022a版本实现从强干扰序列中提取趋势线,并通过可视化展示了原时间序列与提取的趋势分量。代码实现了滑动窗口下的奇异值分解和分组重构,适用于非线性和非平稳时间序列分析。此方法在气候变化、金融市场和生物医学信号处理等领域有广泛应用。
下一篇
无影云桌面