LeetCode 279. 完全平方数
📌 题目描述
题目级别:中等
给你一个整数 n ,返回 和为 n 的完全平方数的最少数量 。
完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。
-
示例 1:
输入:
n = 12输出:
3解释:
12 = 4 + 4 + 4 -
示例 2:
输入:
n = 13输出:
2解释:
13 = 4 + 9
💡 破题思路:动态规划的完全背包模型
这道题本质上是一个完全背包问题的变形:
- 背包容量 :数字
n。 - 物品 :所有的完全平方数(1,4,9,16...1, 4, 9, 16...1,4,9,16...)。
- 物品重量:完全平方数的数值。
- 物品价值:数量统统算作 1 个(因为题目要求"最少数量")。
- 特殊限制:每个物品可以无限次使用。
状态定义:
定义 dp[i] 为:和为 i 的完全平方数的最少数量。
状态转移方程:
对于当前的数字 i,它可以由之前的某个数字加上一个"完全平方数 j×jj \times jj×j"组合而来。
所以我们只需要回头看看之前的状态 dp[i - j*j],然后在这个基础上加 1(也就是加上当前这个完全平方数)。
为了求最少数量,我们在所有可能的回头路径中取最小值:
dp[i] = min(dp[i], dp[i - j * j] + 1)
巧妙的初始化:
dp[0] = 0:凑成 0 需要 0 个数。- 对于其他的
dp[i],我们需要初始化为一个极大值。这里非常巧妙地初始化为n + 1。因为即使全用1来凑,最多也只需要n个数,所以n + 1在这里就起到了"无穷大"的作用,同时完美避免了使用INT_MAX时发生INT_MAX + 1导致的整数溢出问题!
💻 C++ 代码实现
cpp
class Solution {
public:
int numSquares(int n) {
// 使用变长数组 (或可以替换为 vector<int> dp(n + 1, n + 1))
int dp[n + 10];
dp[0] = 0;
// 巧妙初始化为 n + 1,相当于安全版的无穷大
for (int i = 1; i <= n; i ++ ) dp[i] = n + 1;
// 遍历背包容量 (从 1 到 n)
for (int i = 1; i <= n; i ++ )
{
// 遍历物品 (完全平方数 j*j)
for (int j = 1; j * j <= i; j ++ )
{
// 状态转移:取当前值与"刨除完全平方数后剩余值的最少数量 + 1"的较小者
dp[i] = min(dp[i], dp[i - j * j] + 1);
}
}
return dp[n];
}
};