代码随想录–动态规划部分
day 38 休息
day 39 动态规划第六天
一、力扣322–零钱兑换
代码随想录题目链接:代码随想录
给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。
计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。
你可以认为每种硬币的数量是无限的。
硬币数量无限,说明是完全背包
dp[j]
的含义就是凑成j
所需要的最少的硬币个数,背包容量上限为amount
当遍历到coins[i]
时,凑足j - coins[i]
面额的最少个数为dp[j - coins[i]]
,所以递推公式为
d
p
[
j
]
=
m
i
n
(
d
p
[
j
]
,
d
p
[
j
−
c
o
i
n
s
[
i
]
]
+
1
)
dp[j] = min(dp[j], dp[j-coins[i]] + 1)
dp[j]=min(dp[j],dp[j−coins[i]]+1)
+1是因为要用coins[i]这个硬币
这里看到是min了,所以初始化时应该用INT_MAX填充数组
但是第一位应该是0,即dp[0] = 0,因为凑足0元一定是用0个硬币
套公式写就好了
代码如下:
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
vector<int> dp(amount + 1, INT_MAX);
dp[0] = 0;
for(int i = 0; i < coins.size(); i ++)
for(int j = coins[i]; j < amount + 1; j ++)
if (dp[j - coins[i]] != INT_MAX) dp[j] = min(dp[j], dp[j - coins[i]] + 1);
if (dp[amount] == INT_MAX) return -1;
return dp[amount];
}
};
这里if (dp[j - coins[i]] != INT_MAX)
是为了判断是否初始化了,如果没有初始化就跳过这个
二、力扣279–完全平方数
代码随想录题目链接:代码随想录
给你一个整数 n ,返回 和为 n 的完全平方数的最少数量 。
完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。
背包容量为n,物品价值为 i 2 i^2 i2,且物品无限,是完全背包,且无所谓排序还是组合
和上题一样,只是在物品遍历处注意上限是i*i<n
代码如下:
class Solution {
public:
int numSquares(int n) {
vector<int> dp(n + 1, INT_MAX);
dp[0] = 0;
for(int i = 0; i * i <= n; i ++ )
for(int j = i * i; j < n + 1; j ++)
if(dp[j - i*i] != INT_MAX)
dp[j] = min(dp[j], dp[j - i * i] + 1);
return dp[n];
}
};
三、力扣139–单词拆分
代码随想录题目链接:代码随想录
给你一个字符串 s 和一个字符串列表 wordDict 作为字典。如果可以利用字典中出现的一个或多个单词拼接出 s 则返回 true。
单词可以重复使用,是完全背包问题
并且不用考虑单词拆分的问题,字典中的词拼起来不能合成s就返回false即可
dp[i]代表长度为i时,若为true则可以组成,否则不行。其实难点在于遍历
遍历想法可以是:当确认dp[j] = true
,且[j,i]
这个区间的子串出现在字典中,那么dp[i]
也应该是true
这样dp的每个元素可以代表出来字符串s在哪些位置上是可以由字典组成的,检查最后一位就可以了
代码如下:
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 i = 1; i < s.size() + 1; i ++)
for(int j = 0; j < i; j ++)
{
string subs = s.substr(j, i - j);
if(dp[j] && wordSet.find(subs) != wordSet.end())
dp[i] = true;
}
return dp[s.size()];
}
};
四、卡码网56–携带矿石资源
代码随想录题目链接:代码随想录
你是一名宇航员,即将前往一个遥远的行星。在这个行星上,有许多不同类型的矿石资源,每种矿石都有不同的重要性和价值。你需要选择哪些矿石带回地球,但你的宇航舱有一定的容量限制。
给定一个宇航舱,最大容量为 C。现在有 N 种不同类型的矿石,每种矿石有一个重量 w[i],一个价值 v[i],以及最多 k[i] 个可用。不同类型的矿石在地球上的市场价值不同。你需要计算如何在不超过宇航舱容量的情况下,最大化你所能获取的总价值。
物品不是只能使用一次,也不是无限使用,而是有次数了
所以这个题变成了多种背包问题,解决方法就是把多重背包展开成01背包问题
如第i个物品有n件,那就展开成i+n个物品,每个只有一件,且价格一样
代码如下:
#include<iostream>
#include<vector>
using namespace std;
int main() {
int bagWeight,n;
cin >> bagWeight >> n;
vector<int> weight(n, 0);
vector<int> value(n, 0);
vector<int> nums(n, 0);
for (int i = 0; i < n; i++) cin >> weight[i];
for (int i = 0; i < n; i++) cin >> value[i];
for (int i = 0; i < n; i++) cin >> nums[i];
vector<int> dp(bagWeight + 1, 0);
for(int i = 0; i < n; i++) { // 遍历物品
for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量
// 以上为01背包,然后加一个遍历个数
for (int k = 1; k <= nums[i] && (j - k * weight[i]) >= 0; k++) { // 遍历个数
dp[j] = max(dp[j], dp[j - k * weight[i]] + k * value[i]);
}
}
}
cout << dp[bagWeight] << endl;
}