代码随想录day38dp6
文章目录
- 322. 零钱兑换
- 279.完全平方数
- 139.单词拆分
- 携带矿石资源(第八期模拟笔试)
322. 零钱兑换
题目链接
文章讲解
class Solution {
public:int coinChange(vector<int>& coins, int amount) {// 1. 确定 DP 数组及下标的含义:// dp[j] 表示组成金额 j 所需的最小硬币数量。// dp[amount] 表示凑成目标金额 amount 的最小硬币数,最终返回的就是这个值。// 初始化 dp 数组,大小为 amount + 1,所有元素初始化为 INT_MAX// dp[0] = 0 表示凑成金额 0 的方法是 0 个硬币(即不选任何硬币)vector<unsigned long long> dp(amount + 1, INT_MAX);dp[0] = 0; // 组成金额 0 不需要任何硬币// 2. 确定递推公式:// dp[j] = min(dp[j], dp[j - coin] + 1)// dp[j] 表示凑成金额 j 所需的最小硬币数。// 通过遍历每个硬币,如果使用当前硬币,可以减少所需硬币的数量。// 对每个金额 j,尝试使用当前硬币 coin,并更新 dp[j] 为最小值。// 遍历每个硬币for (int i = 0; i < coins.size(); i++) { // 遍历每个硬币// 遍历金额 j,从 coins[i] 到 amountfor (int j = coins[i]; j <= amount; j++) {// 更新 dp[j],即尝试用当前硬币来构成金额 jdp[j] = min(dp[j], dp[j - coins[i]] + 1); // 如果 dp[j - coins[i]] 不是 INT_MAX,更新 dp[j]}}// 3. 最终结果:// 如果 dp[amount] 仍然是 INT_MAX,表示无法凑成金额 amount,返回 -1if (dp[amount] == INT_MAX) return -1;// 返回 dp[amount],即凑成目标金额 amount 的最小硬币数return dp[amount];}
};
279.完全平方数
题目链接
文章讲解
class Solution {
public:int numSquares(int n) {// 1. 确定 DP 数组及下标的含义:// dp[j] 表示凑出金额 j 所需的最小完全平方数个数。// dp[n] 就是最终需要的结果,表示最少需要多少个完全平方数来凑成 n。// 2. 计算所有的完全平方数:vector<int> nums;for (int i = 1; i <= sqrt(n); i++) {nums.push_back(i * i); // 将所有小于等于 n 的完全平方数加入 nums 数组}// 3. dp 数组如何初始化:// dp 数组的大小为 n+1,表示从 0 到 n 的所有金额。// 初始值设为 INT_MAX,表示暂时没有找到凑出该金额的组合,dp[0] = 0 代表凑出 0 的方法是 0。vector<unsigned long long> dp(n + 1, INT_MAX);dp[0] = 0; // 组成金额 0 不需要任何完全平方数// 4. 确定递推公式:// 对于每个完全平方数 num,我们遍历每个金额 j,从 num 到 n。// dp[j] = min(dp[j], dp[j - num] + 1),表示如果当前金额 j 可以通过 num 这个完全平方数来构成,// 那么更新 dp[j] 为 dp[j - num] + 1(即在 dp[j - num] 的基础上再加上 1 个 num)。for (auto num : nums) {for (int j = num; j <= n; j++) {dp[j] = min(dp[j], dp[j - num] + 1); // 更新 dp[j] 为最小的组合数}}// 5. 最终结果:// dp[n] 就是最小的完全平方数个数,返回 dp[n] 即可。return dp[n];}
};
139.单词拆分
题目链接
文章讲解
class Solution {
public:bool wordBreak(string s, vector<string>& wordDict) {int n = s.size();// 1. 确定 DP 数组及下标的含义:// dp[i] 表示从字符串 s 的前 i 个字符中是否能分割成字典中的单词。// dp[n] 就是最终的结果,表示字符串 s 是否可以被完全分割成字典中的单词。vector<bool> dp(n + 1, false); // 初始化 dp 数组,大小为 n + 1,初始值为 falsedp[0] = true; // dp[0] 为 true,表示空字符串可以被分割(基准情况)// 2. 确定递推公式:// dp[i] = true 如果存在 j (0 <= j < i) 使得 dp[j] 为 true 且 s[j, i)(即 s 从 j 到 i 的子串)在 wordDict 中。// 即:如果我们从 s[0] 到 s[j] 能被分割(dp[j] 为 true),且 s[j, i) 是一个有效单词,则 dp[i] 为 true。unordered_set<string> words(wordDict.begin(), wordDict.end()); // 使用 unordered_set 来存储字典中的单词,查找效率更高for (int i = 1; i <= n; i++) { // 遍历每个位置 ifor (int j = 0; j < i; j++) { // 对于每个 i,尝试遍历前面的所有位置 jstring word = s.substr(j, i - j); // 获取 s[j, i) 子串if (words.find(word) != words.end() && dp[j]) { // 如果 s[j, i) 是字典中的单词并且 dp[j] 为 truedp[i] = true; // 更新 dp[i] 为 true,表示 s[0, i) 可以被分割break; // 一旦找到符合条件的 j,直接跳出循环,dp[i] 已经确定}}}// 3. 最终结果:// 返回 dp[n],即是否能完全分割字符串 sreturn dp[n]; // 如果 dp[n] 为 true,表示整个字符串可以分割}
};
多重背包问题转换为01背包
携带矿石资源(第八期模拟笔试)
题目链接
文章讲解
#include <bits/stdc++.h>
using namespace std;typedef long long ll;// 定义一个名为solve的函数
void solve() {ll c, n;cin >> c >> n; // 1. 读取背包容量 c 和物品种类数 nvector<int> w; // 物品的重量数组vector<int> v; // 物品的价值数组// 2. 读取物品的重量for (int i = 0; i < n; i++) {int x;cin >> x; // 读取物品的重量w.push_back(x); // 将物品的重量加入到 w 数组}// 3. 读取物品的价值for (int i = 0; i < n; i++) {int x;cin >> x; // 读取物品的价值v.push_back(x); // 将物品的价值加入到 v 数组}// 4. 读取每种物品的数量并拆分物品for (int i = 0; i < n; i++) {int x;cin >> x; // 读取物品的数量x--; // 将数量减 1,以便生成多个物品while (x--) {w.push_back(w[i]); // 将当前物品的重量加入到 w 数组v.push_back(v[i]); // 将当前物品的价值加入到 v 数组}}// 5. 初始化 dp 数组:// dp[j] 表示容量为 j 时,背包可以获得的最大价值vector<int> dp(c + 1, 0); // dp 数组,初始值为 0// 6. 遍历所有物品,更新 dp 数组for (int i = 0; i < w.size(); i++) { // 遍历所有物品for (int j = c; j >= w[i]; j--) { // 逆序遍历容量 j,防止重复选择同一物品dp[j] = max(dp[j], dp[j - w[i]] + v[i]); // 更新 dp 数组,选择最大价值}}// 7. 输出背包最大容量 c 时的最大价值cout << dp[c]; // 最终结果,即背包最大容量 c 时的最大价值
}int main() {cin.tie(0); cout.tie(0);ios::sync_with_stdio(false); // 提高 cin、cout 的输入输出效率solve(); // 调用 solve 函数执行具体操作
}