当前位置:   article > 正文

代码随想录算法训练营Day46|139.单词拆分、多重背包理论基础、背包问题总结

代码随想录算法训练营Day46|139.单词拆分、多重背包理论基础、背包问题总结

目录

139.单词拆分

方法一:回溯法

算法实现

方法二:背包问题

算法实现

多重背包理论基础

思路

算法实现

背包问题总结

前言

背包递推公式

遍历顺序

0-1背包

完全背包


139.单词拆分

题目链接

文章链接

方法一:回溯法

        在回溯专题中分割回文串与本题有点类似,分割回文串是枚举分割后的所有子串,判断是否回文;本题是枚举分割所有字符串,判断是否在字典里出现过。因此也可以使用回溯法进行实现。

算法实现
  1. class Solution {
  2. private:
  3. bool backtracking(const string& s, const unordered_set<string>& wordSet, vector<bool>& memory, int startIndex) {
  4. if (startIndex >= s.size()) {
  5. return true;
  6. }
  7. if (!memory[startIndex]) return memory[startIndex];
  8. for (int i = startIndex; i < s.size(); i++) {
  9. string word = s.substr(startIndex, i - startIndex + 1);
  10. if (wordSet.find(word) != wordSet.end() && backtracking(s, wordSet, memory, i + 1)) {
  11. return true;
  12. }
  13. }
  14. memory[startIndex] = false;
  15. return false;
  16. }
  17. public:
  18. bool wordBreak(string s, vector<string>& wordDict) {
  19. unordered_set<string> wordSet(wordDict.begin(), wordDict.end());
  20. vector<bool> memory(s.size(), 1);
  21. return backtracking(s, wordSet,memory, 0);
  22. }
  23. };

        递归的过程中有很多重复计算,可以使用数组保存一下递归过程中计算的结果,这个叫做记忆化递归。

        使用memory数组保存每次计算的以startIndex起始的计算结果,如果memory[startIndex]里已经被赋值了,直接用memory[startIndex]的结果。

方法二:背包问题

         单词就是物品,字符串s就是背包,单词能否组成字符串s,就是问物品能不能把背包装满。拆分时可以重复使用字典中的单词,说明就是一个完全背包!

        利用动规五部曲进行分析:

1.确定dp数组及其下标的含义:

        dp[i] : 字符串长度为i的话,dp[i]为true,表示可以拆分为一个或多个在字典中出现的单词

2.确定递推公式:

        如果确定dp[j] 是true,且 [j, i] 这个区间的子串出现在字典里,那么dp[i]一定是true。(j < i )。所以递推公式是:if([j, i] 这个区间的子串出现在字典里 && dp[j]是true) 那么 dp[i] = true。

3.初始化dp数组:

        有递推公式可知,dp[i]的值要依赖dp[j],因此dp[0]是dp数组后面所有值的基础,必须为true,其它下标先初始化为false;

4.确定遍历顺序:

        对于一个有子串组成的字符串,各子串的前后位置不同会使构成的字符串不同,相当于往背包中放物品的顺序有要求,因此是排列问题。

        既然是排列问题,那么就要先遍历背包,再遍历物品。

5.打印dp数组:

        以输入: s = "leetcode", wordDict = ["leet", "code"]为例,dp状态如图:

算法实现

 

  1. class Solution {
  2. public:
  3. bool wordBreak(string s, vector<string>& wordDict) {
  4. unordered_set<string> wordSet(wordDict.begin(), wordDict.end());
  5. vector<bool> dp(s.size() + 1, false);
  6. dp[0] = true;
  7. for (int i = 1; i <= s.size(); i++) {
  8. for (int j = 0; j < i; j++) {
  9. string word = s.substr(j, i - j);
  10. if (wordSet.find(word) != wordSet.end() && dp[j]) {
  11. dp[i] = true;
  12. }
  13. }
  14. }
  15. return dp[s.size()];
  16. }
  17. };

多重背包理论基础

题目链接

文章链接

思路

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

        多重背包实际就是0-1背包的变形,因为每件物品最多有Mi件可用,把Mi件摊开,其实就是一个01背包问题了。

算法实现

  1. # include <bits/stdc++.h>
  2. using namespace std;
  3. int main() {
  4. int bagweight, n;
  5. cin >> bagweight >> n;
  6. vector<int> weight(n, 0);
  7. vector<int> value(n, 0);
  8. vector<int> nums(n, 0);
  9. for (int i = 0; i < weight.size(); i++) cin >> weight[i];
  10. for (int i = 0; i < value.size(); i++) cin >> value[i];
  11. for (int i = 0; i < nums.size(); i++) cin >> nums[i];
  12. vector<int> dp(bagweight + 1, 0);
  13. for (int i = 0; i < n; i++){
  14. for (int j = bagweight; j >= weight[i]; j--){
  15. // 以上为0-1背包,再加一个for循环遍历个数
  16. for (int k = 1; k <= nums[i] && (j - k * weight[i]) >= 0; k++) {
  17. dp[j] = max(dp[j - k * weight[i]] + k * value[i], dp[j]);
  18. }
  19. }
  20. }
  21. cout << dp[bagweight] << endl;
  22. }

背包问题总结

前言

        这几天学的背包问题主要有三种:0-1背包、完全背包、多重背包。

        这三种背包问题的主要区别在于:0-1背包问题所有物品只有一件,每件物品至多只能存取一次;而完全背包问题所有物品都有无限件,可以对任意满足条件的背包取用任意次;多重背包问题每件物品都有一定的数量。

        对于背包问题,尤其要注意动规五部曲中第二步——确定递推公式和第四步——确定遍历顺序。

背包递推公式

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

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

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

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

遍历顺序

0-1背包

        二维dp数组01背包先遍历物品还是先遍历背包都是可以的,且第二层for循环是从小到大遍历。而解一维dp数组01背包只能先遍历物品再遍历背包容量,且第二层for循环是从大到小遍历。

完全背包

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

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

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

        如果求最小数,那么两层for循环的先后顺序就无所谓了。

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

闽ICP备14008679号