代码随想录算法训练营第33天|322. 零钱兑换、279.完全平方数、139.单词拆分
LeetCode322
2025-03-06 19:01:34 星期四
题目描述:力扣322
文档讲解:代码随想录(programmercarl)322. 零钱兑换
视频讲解:《代码随想录》算法视频公开课:装满背包最少的物品件数是多少?| LeetCode:322.零钱兑换
代码随想录视频内容简记
这个题特殊的地方在要求的是最少的物品数量,和一零和有点像,一零和求的是最大的物品数量
梳理
-
确定dp[j]数组的含义,表示背包容量为j装满所能放的最少物品数量为dp[j]
-
确定递推公式,
dp[j] = min(dp[j], dp[j - coins[i]] + 1)
。解释一下dp[j - coins[i]] + 1
的含义,和一零和的dp[i - x][j - y] + 1
很像,coins[i]表示重量,+ 1是因为dp数组表示物品的数量,一次只能+1 -
初始化dp[j]数组,dp[0] = 0表示背包容量为j所能放的最少物品数量为0,是符合题意的。另外,对于非零下标,因为本题求的是最小的min,所以后面初始化不能直接用0,而应该用INT_MAX
-
确定遍历顺序,首先和474一零和不一样的地方就在于本题是一个完全背包问题,所以对先遍历物品或者先遍历背包所求的一个是组合数,一个是排列数。但是因为本题所求的是最少的物品数量,和具体的组合方法个数不一样,所以那种遍历方式都可以
-
打印dp数组
LeetCode测试
点击查看代码
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
vector<uint64_t> dp(amount + 1, INT_MAX);
dp[0] = 0;
for (int i = 0; i < coins.size(); i++) {
for (int j = 0; j <= amount; j++) {
if (j < coins[i]) dp[j] = dp[j];
else dp[j] = min(dp[j], dp[j - coins[i]] + 1);
}
}
if (dp[amount] == INT_MAX) return -1;
return dp[amount];
}
};
LeetCode279
题目描述:力扣279
文档讲解:代码随想录(programmercarl)279.完全平方数
视频讲解:《代码随想录》算法视频公开课:换汤不换药!| LeetCode:279.完全平方数
代码随想录视频内容简记
本题可以抽象为一个完全背包问题,首先整数n表示背包的最大容量为n,而每个物品的价值和重量都是一样的,可以像爬楼梯进阶那样进行先遍历写好其平方的数组
梳理
-
确定dp[j]数组的含义,表示容量为j的背包装满需要的最少物品数量为dp[j]
-
确定递推公式,和上面的322一样,
dp[j] = min(dp[j], dp[j - nums[i]] + 1)
-
初始化dp数组,
dp[0] = 0
,剩余非零下边的值都初始化最大INT_MAX -
确定遍历顺序,先遍历物品或者先遍历背包都可以
-
打印dp数组
LeetCode测试
刚开始写的是这样的,这样写的话数组定义那里总是出错,改成push_back就好了。
需要注意的是,在for循环遍历添加的时候,需要i * i <= n
,因为有一个测试用例是n = 1
,所以必须得加上等于号
点击查看代码
class Solution {
public:
int numSquares(int n) {
vector<int> nums;
for (int i = 1; i * i <= n; i++) {
nums.push_back(i * i);
cout << i * i << endl;
}
cout << nums.size() << endl;
vector<uint64_t> dp(n + 1, INT_MAX);
dp[0] = 0;
for (int i = 0; i < nums.size(); i++) {
for (int j = 0; j <= n; j++) {
if (j < nums[i]) dp[j] = dp[j];
else dp[j] = min(dp[j], dp[j - nums[i]] + 1);
}
}
return dp[n];
}
};
之后又看k哥的视频讲了一遍,思路都是一样的
点击查看代码
class Solution {
public:
int numSquares(int n) {
vector<uint64_t> dp(n + 1, INT_MAX);
dp[0] = 0;
for (int i = 1; i * i <= n; i++) {
for (int j = 0; j <= n; j++) {
if (j < i * i) dp[j] = dp[j];
else dp[j] = min(dp[j], dp[j - i * i] + 1);
}
}
return dp[n];
}
};
LeetCode139
题目描述:力扣139
文档讲解:代码随想录(programmercarl)139.单词拆分
视频讲解:《代码随想录》算法视频公开课:你的背包如何装满?| LeetCode:139.单词拆分
代码随想录视频内容简记
首先确定本题的背包容量就是s字符串的长度,而本题物品的价值就是true或者false,物品的重量就是每一个在字典当中的单词本身,比如“leet”和“code”这两个单词本身,而至于用i遍历,只是通过这样的数值截取来得到单词本身的手段。
梳理
-
确定dp[j]数组的含义,表示串长为j的字符串,其表示的dp[j]为true,也就是可以在wordset总被找到,这样才能拼接s字符串。感觉这个题挺抽象的,感觉这个dp数组的定义就不太好理解
-
确定递推公式,
if (字串存在于集合中 && dp[i] == true) dp[j] = true
,在递推公式中需要判断的有两个,一个是单词必须在wordset中能找到,另外就是前面的dp[i]为true,这样做的目的就是确保前面所有单个单词都已经被在wordset中找到了 -
初始化dp[j]数组,
dp[0] = true
,如果一旦dp[0]为false,那么后面的所有情况机会都变成false了。至于非零下标,全部初始化为false即可 -
确定遍历顺序,本题要求的是一个排列数,组成的单词才会不一样,比如“applepenapple”和“penappleapple”的组合都一样,但是二者属于不同的排列。所以需要先遍历背包,后遍历物品,这也是确保wordset中分割的单词能拼成目标s的前提
-
打印dp数组
LeetCode测试
点击查看代码
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;
// string wo = "applepen";
// cout << "applepen" << wo.substr(5, 3);
for (int j = 0; 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()];
}
};
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端