文章目录
完全背包详解
Leetcode279.完全平方数
1.问题描述
2.解决方案
详细过程在代码sxl中,这一个典型的完全背包并且求的是最小个数
1.求最少个数,那就意味这组合还是排列都一样,所以遍历顺序先后物品背包都可以
2.初始化dp[0]=0,其他用最大值,这个前面也提到很多次了
3.第二层 j 应该从item开始遍历,这个多注意
错误:dp[j] 的定义中 j 时背包容量,所以无论遍历顺序如何 dp[j] 那肯定找准背包,所以一开始没整明白,搞了个dp[i] i 时物品那肯定不对了
//dp[i]=min(dp[i],dp[i-item]+1);
dp[j]=min(dp[j],dp[j-item]+1);
class Solution {
public:
int numSquares(int n) {
//1.
//2.
vector<int> dp(n+1,INT_MAX);
dp[0]=0;
//3.
for(int i=1;i*i<=n;i++){ //先物品
int item=i*i;
for(int j=item;j<=n;j++){ //后正序背包
//dp[i]=min(dp[i],dp[i-item]+1);
dp[j]=min(dp[j],dp[j-item]+1);
}
}
//4.
return dp[n];
}
};
Leetcode139.单词拆分
1.问题描述
2.解决方案
想法一:自己的想法
代码sxl上有我的思路,正确的递推式子但是由于字符串操作不熟悉没实现
dp[i] = dp[i] || dp[i-遍历到的物品字符串]
即dp[i]是true当且仅当,取当前物品字符串为真或者不取当前字符串为真为真,有一个为真即可
//自己的想法(想法没毛病,就是字符串操作不熟悉和没想明白)
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict) {
//1.
int len=s.size();
//2.
vector<bool> dp(len+1, false);
//3.
for (auto item : wordDict) {
for(int i=item.size();i<=len;i++){
//取到s[0]到s[i]闭区间
string bag(s.begin(),s.begin()+i+1);
vector<string> Bag(s.begin(),s.begin()+i+1);
int index=bag.find(item);
if(index!=string::npos){
}
}
}
//4.
}
};
想法二:正确的递推方式
代码sxl有详细解答
1.递推方式太优秀了,dp[i] = [j,i]这个字串出现在物品中 && dp[j]==true
2.初始化方式老生常谈
3.遍历方式很有说法,因为题目特殊性,需要验证是否为背包的子集,所以要先背包
//代码sxl完全背包
class Solution{
public:
bool wordBreak(string s, vector<string>& wordDict) {
//1.为了好查找,因为vector不可以查找
int len=s.size();
unordered_set<string> goods(wordDict.begin(),wordDict.end());
//2.
vector<bool> dp(len+1, false);
dp[0]=true;
//3.由于题目特殊性要看看wordDict里面是不是s的子集,所以选择把s放在外层也就是先遍历背包
for(int i=0;i<=len;i++){
for(int j=0;j<=i;j++){
string word=s.substr(j,i-j);
if(goods.find(word)!=goods.end()&&dp[j]==true) dp[i]=true;
}
}
//4.
return dp[len];
}
};
Leetcode322.零钱兑换
1.问题描述
2.解决方案
解法一:自己的dp(仿造完全背包问题)
1.dp数组没问题
2.递归没问题
3.dp数组初始化,要用最大值,因为求的是min(),如果不用最大容易无法更新到这个值
vector<int> dp(amount+1, INT_MAX);
dp[0]=0;
4.遍历问题(这个代码随想录中有总结)
5.如果是初始值,可以直接跳
if(dp[i-item]==INT_MAX) continue;
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
//1.
int len=coins.size();
//2.
vector<int> dp(amount+1, INT_MAX);
dp[0]=0;
for (auto item: coins) {
for(int i=item;i<=amount;i++){
if(dp[i-item]==INT_MAX) continue;
dp[i]=min(dp[i],dp[i-item]+1);
}
}
//3.
if(dp[amount]==INT_MAX) return -1;
return dp[amount];
}
};
解法二:官方的dp(和解法一自己的dp代码相似,但是思路大不相同,没看)
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
int Max = amount + 1;
vector<int> dp(amount + 1, Max);
dp[0] = 0;
for (int i = 1; i <= amount; ++i) {
for (int j = 0; j < (int)coins.size(); ++j) {
if (coins[j] <= i) {
dp[i] = min(dp[i], dp[i - coins[j]] + 1);
}
}
}
return dp[amount] > amount ? -1 : dp[amount];
}
};
Leetcode518.零钱兑换 II(求装满背包的方法数)
1.问题描述
2.解决方案
1.主要是把此问题转化成完全背包问题
2.遍历顺序问题,先物品后背包得到组合数(无顺序),先背包后物品得到排列数(有顺序)
3.递推是取不取相加,对应边界dp[0]=1,通过含义也可以得到或者由于是递推的终点所以必须是1
4.其他值初始化,由于是求相加为了不影响肯定是0了
class Solution {
public:
int change(int amount, vector<int>& coins) {
//1.
//int len=coins.size();
//2.
vector<int> dp(amount+1,0);
dp[0]=1;
//3.
for (auto item : coins) {
for(int i=item;i<=amount;i++){
dp[i]=dp[i]+dp[i-item];
}
}
//4.
return dp[amount];
}
};
Leetcode377.组合总和 Ⅳ
1.问题描述
2.解决方案
1.这个题除了遍历顺序和上一题518一摸一样,518求的是组合数,这一题求的是排列数,只需要将遍历顺序变为先背包后物品
2.一个需要注意的点,就是只有当 i>=item 背包值大于物品值时才有取和不取两种选择,为dp[i]=dp[i]+dp[i-item] ,如果是 i<item 即 背包值小于物品值,只有不取这一种选择,即恒为 dp[i]=dp[i],当然这一部分可以不变可以直接不写,但是要清楚为什么
if(i>=item&&dp[i]<(INT32_MAX-dp[i-item])) dp[i]=dp[i]+dp[i-item];
if(i<item) dp[i]=dp[i];
3.数据溢出的问题,两个数都是int相加大于了int会报溢出的错误,所以要加上溢出判断,以下为两个版本的判断,很明显第一种 (dp[i]+dp[i-item])<INT32_MAX 不对因为虽然是判读了,但是相加操作以及进行了已经溢出了
//if(i>=item&&(dp[i]+dp[i-item])<INT32_MAX) dp[i]=dp[i]+dp[i-item];
if(i>=item&&dp[i]<(INT32_MAX-dp[i-item])) dp[i]=dp[i]+dp[i-item];
class Solution {
public:
int combinationSum4(vector<int>& nums, int target) {
//1.
//int len=nums.size();
//2.
vector<int> dp(target+1,0);
dp[0]=1;
//3.
for(int i=0;i<=target;i++){
for (auto item : nums) {
//if(i>=item&&(dp[i]+dp[i-item])<INT32_MAX) dp[i]=dp[i]+dp[i-item];
if(i>=item&&dp[i]<(INT32_MAX-dp[i-item])) dp[i]=dp[i]+dp[i-item];
//if(i<item) dp[i]=dp[i];
}
}
//4.
return dp[target];
}
};