139.单词拆分
视频讲解: 动态规划之完全背包,你的背包如何装满?| LeetCode:139.单词拆分_哔哩哔哩_bilibili代码随想录
解题思路
1.dp[i] 字符串的长度为i,dp[i]是否可以被组成
2.递推公式
if( [j,i] && dp[j] =true) 这个ji区间在字典中,并且dp[j] = true
3.初始化
dp[0] = true 在题目中没有定义,但是为了推导,初始为true
非零下标为false
4.遍历循序
装满这个背包,对物品的顺序是有要求的,因此这题是排列
for( i<=s.size() )
for(j=0 ; j<i ;j ++)
string word = s.substr(j,i-j); 进行截取
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict) {
unordered_set<string> wordSet(wordDict.begin(),wordDict.end());
vector<bool> dp(s.size()+1,false);
dp[0] = true;
for(int j=1 ; j<=s.size();j++)
{
for(int i = 0 ; i < j ; i++)
{
string word = s.substr(i,j-i);
if(wordSet.find(word) != wordSet.end() && dp[i]==true)
dp[j] = true;
}
}
return dp[s.size()];
}
};
多重背包
和01背包是很相似的
但是这样拆分为01背包很容易超时,因为vector的动态扩容,因此我们只需要再加一层循环,遍历物品个数即可
背包问题总结
代码随想录
01背包:2维dp为什么可以转换为1维dp?
1维dp利用的是滚动数组,由于我们2维dp中,dp[i][j]的值都是由上方或者左上方的某一个值推来的,因此只需要两行数据,然后我们可以将上一行数据拷贝存在一行数据中,动态更新即可
01背包:为什么遍历顺序必须是先物品再背包,为什么遍历背包时,需要倒序遍历?
首先,为什么要倒序遍历?因此由二维dp压缩为一维dp可知,一维dp的值是上一行的数值,因此如果我们从左边正序更新,那么就会丢失上一行数据,无法推出这一行的数据。但我们如果从右边倒序遍历,那么左边的值还未更新,因此是上一行数据,可以推出这一行数据
其次,为什么顺序必须物品,再背包?因为只有这样,物品只会被选取1次(更新完数据了,新数据是由旧数据推来的,因此视为1次),如果反一下,先背包再物品,那么就会变成装满这个容量的背包,有哪些物品
完全背包: 遍历顺序和for循环内部与01背包的不同
与01背包的不同:我们遍历背包时是正序遍历,从weight[i]开始,因为完全背包物品可以使用无数次,每次都是由新数据(一维dp左边的数据推得而来,视为可以使用无数次)推的而来,因此是正序
如果是纯完全背包问题(求最大价值),遍历顺序是可以颠倒的,因为不管是先行还是先列推导,都可以得到左边的数据
但如果题目求得是组合问题,那么就必须先物品再容量:例如先遍历物品1,再遍历物品2,只会出现[1,2]的情况
如果求得是排列问题,那么就必须先容量再物品,因为假如weight[2]>weight[1],那么在容量为3的情况下,就可能出现[2,1]和[1,2]两种不同的组合,因此是排列
背包添加额外限制条件
因为我们平时都是一维dp[j],只有容量一个限制,如果当我们有别的限制,例如有两个维度的容量,或者对选取物品种类个数有要求,那么就dp[j][k],利用二维dp,多加限制
收获
背包问题结束,继续加油