1. 单词拆分
s
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict) {
unordered_set<string> wordSet(wordDict.begin(), wordDict.end());
// 1. dp[i] 为true代表 可以拆分为一个或者多个在字典中出现的单词
vector<bool> dp(s.size()+1, false);
dp[0] = true;
// 2.确定递推公式
// 如果dp[j]是true并且, dp[j, i]在字典中出现过,那么一定为true
// 所以公式应该为 if dp[j] && dp[j, i]在字典中出现过
for(int i=1; i<=s.size(); i++){ // 先遍历背包ssssssssssssssssssssss
for(int j=0; j<i; j++){
string word = s.substr(j, i-j);
if(dp[j] && wordSet.find(word) != wordSet.end()){
dp[i] = true;
}
}
}
return dp[s.size()];
// 3.初始化数组 dp[i]全部初始化为false, dp[0]初始化为true
// 4.确定遍历顺序(组合是外层物品内层背包,排序是外层背包内层物品)
// 本题applepenapple 必须由 apple pen apple 按顺序组合,因此本题属于排序问题
}
};
2.多重背包
多重背包就是 可以重复使用物品,但是物品的数量是有限制的
因此可以将其转换为01 背包的问题,即将物品的数量展开
void mul_bag_proble() {
vector<int> weight = { 1 , 3 , 4 };
vector<int> value = { 15, 20, 30 };
vector<int> nums = { 2 ,3, 2 };
int bagweight = 10;
for (int i = 0; i < nums.size(); i++) {
while (nums[i] > 1) {
weight.push_back(weight[i]);
value.push_back(value[i]);
nums[i]--;
}
}
vector<int> dp(bagweight+1, 0);
for (int i = 0; i < weight.size(); i++) { // 先遍历物品
for (int j = bagweight; j >= weight[i]; j--) {
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
}
for (int j = 0; j <= bagweight; j++) {
cout << dp[j] << ", ";
}
cout << endl;
}
cout << dp[bagweight] << endl;
}
3. 背包总结
- 背包的种类
- 背包的递推公式
- 问背包是否能够装满 或者 最多可以装多少 max(dp[j], dp[j-i] + value[i])
- 装满背包有多少种方法: dp[j] += dp[j-i]
- 背包装满的最大价值: dp[j] = max(dp[j], dp[j-weight[i]]+value[i])
- 装满背包的最小数: dp[j] = min(dp[j-coins[i] +1, dp[j])
- 遍历顺序
- 01背包
- 二维dp数组:先遍历物品还是背包都可以,二层for循环是从小到大遍历
- 一维dp数组:必须先遍历物品再遍历背包, 并且背包是从大到小的遍历
- 完全背包
- 求组合数: 外层for循环物品,内层循环背包容量
- 求排序: 外层for循环背包, 内层for循环物品
- 求最小数: 两个for循环顺序和大小都可以
- 01背包