题目
中等
相关标签
给你一个整数 n
,返回 和为 n
的完全平方数的最少数量 。
完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1
、4
、9
和 16
都是完全平方数,而 3
和 11
不是。
示例 1:
输入:n = 12
输出:3
解释:12 = 4 + 4 + 4
示例 2:
输入:n = 13
输出:2
解释:13 = 4 + 9
提示:
1 <= n <= 104
思路和解题方法
- 在两个版本中,都使用了一个一维数组
dp
来保存状态。其中,dp[i]
表示当目标数为i
时,需要的最小完全平方数和。初始值为INT_MAX
表示不可达。- 在第一个版本中,我们通过遍历背包和物品来更新状态。外层循环遍历背包,内层循环遍历物品,对于每一个背包
i
,我们尝试将某个完全平方数j*j
放入背包,然后更新状态。递推公式为:dp[i] = min(dp[i - j * j] + 1, dp[i])
,表示将j*j
放入背包后,需要的完全平方数和为dp[i - j * j] + 1
,与之前的状态dp[i]
比较取较小值。- 而在第二个版本中,我们通过遍历物品和背包来更新状态。外层循环遍历物品,内层循环遍历背包,对于每一个物品
i*i
,我们尝试将其放入背包j
中,然后更新状态。递推公式为:dp[j] = min(dp[j - i * i] + 1, dp[j])
,表示将i*i
放入背包j
后,需要的完全平方数和为dp[j - i * i] + 1
,与之前的状态dp[j]
比较取较小值。- 两个版本的代码实现思路是一样的,只是遍历的顺序不同,但最终得到的结果是相同的。
复杂度
时间复杂度:
O(n * sqrt(n)) (sqrt()为开方函数)
时间复杂度:
两个版本的代码都使用了嵌套循环。最外层的循环遍历的次数都是目标数
n
,内层的循环遍历的次数都是完全平方数的个数,而最大的完全平方数为sqrt(n)
,因此内层循环的时间复杂度为 O(sqrt(n))。因此,两个版本的代码的时间复杂度都是 O(n * sqrt(n))。
空间复杂度
O(n)
空间复杂度:
两个版本的代码都使用了一个一维数组
dp
来记录状态。数组的长度为n + 1
,因此空间复杂度为 O(n)。而其他变量的空间复杂度都是常数级别的。
c++ 代码
cpp
// 版本一
class Solution {
public:
int numSquares(int n) {
vector<int> dp(n + 1, INT_MAX); // 创建一个大小为 n+1 的数组,初始化为 INT_MAX,表示不可达状态
dp[0] = 0; // 初始条件,当目标数为 0 时,需要的最小完全平方数和为 0
for (int i = 0; i <= n; i++) { // 遍历背包,i 表示当前目标数
for (int j = 1; j * j <= i; j++) { // 遍历物品,j*j 表示完全平方数
dp[i] = min(dp[i - j * j] + 1, dp[i]); // 更新状态,取较小值
}
}
return dp[n]; // 返回目标数为 n 时的最小完全平方数和
}
};
// 版本二
class Solution {
public:
int numSquares(int n) {
vector<int> dp(n + 1, INT_MAX); // 创建一个大小为 n+1 的数组,初始化为 INT_MAX,表示不可达状态
dp[0] = 0; // 初始条件,当目标数为 0 时,需要的最小完全平方数和为 0
for (int i = 1; i * i <= n; i++) { // 遍历物品,i 表示当前物品,即完全平方数
for (int j = i * i; j <= n; j++) { // 遍历背包,j 表示当前目标数
dp[j] = min(dp[j - i * i] + 1, dp[j]); // 更新状态,取较小值
}
}
return dp[n]; // 返回目标数为 n 时的最小完全平方数和
}
};
Java代码
java
class Solution {
// 版本一,先遍历物品, 再遍历背包
public int numSquares(int n) {
int max = Integer.MAX_VALUE;
int[] dp = new int[n + 1];
// 初始化
for (int j = 0; j <= n; j++) {
dp[j] = max;
}
// 当和为0时,组合的个数为0
dp[0] = 0;
// 遍历物品
for (int i = 1; i * i <= n; i++) {
// 遍历背包
for (int j = i * i; j <= n; j++) {
// 更新状态,取较小值
dp[j] = Math.min(dp[j], dp[j - i * i] + 1);
// 不需要这个if判断语句,因为在完全平方数这一问题中不会有"凑不成"的情况发生(一定可以用"1"来组成任何一个n),所以注释掉了这个if语句。
}
}
return dp[n]; // 返回目标数为 n 时的最小完全平方数和
}
}
class Solution {
// 版本二, 先遍历背包, 再遍历物品
public int numSquares(int n) {
int max = Integer.MAX_VALUE;
int[] dp = new int[n + 1];
// 初始化
for (int j = 0; j <= n; j++) {
dp[j] = max;
}
// 当和为0时,组合的个数为0
dp[0] = 0;
// 遍历背包
for (int j = 1; j <= n; j++) {
// 遍历物品
for (int i = 1; i * i <= j; i++) {
// 更新状态,取较小值
dp[j] = Math.min(dp[j], dp[j - i * i] + 1);
}
}
return dp[n]; // 返回目标数为 n 时的最小完全平方数和
}
}
觉得有用的话可以点点赞,支持一下。
如果愿意的话关注一下。会对你有更多的帮助。
每天都会不定时更新哦 >人< 。