动态规划—279. 完全平方数
题目描述
前言
完全平方数问题 是一个经典的动态规划问题。给定一个正整数 n
,要求将其表示为若干个完全平方数的和,且要求这些完全平方数的数量最少。例如:12 = 4 + 4 + 4
,因此 12
可以表示为 3 个完全平方数的和。
我们可以利用动态规划的思想,逐步构建出最优解,即找到使得 n
表示为最少的完全平方数的方案。
基本思路
1. 问题定义
给定一个正整数 n
,要求将其分解为若干个完全平方数的和,并且完全平方数的数量最少。输出最少的完全平方数数量。
举例:
-
输入:
n = 12
-
输出:
3
,因为12 = 4 + 4 + 4
-
输入:
n = 13
-
输出:
2
,因为13 = 4 + 9
2. 理解问题和递推关系
核心思路:
对于给定的 n
,我们可以依次减去某个完全平方数,剩余的部分继续递归地寻找最优解。我们需要找到所有完全平方数中的最小组合。
动态规划状态定义:
dp[i]
:表示数字i
能够表示为若干个完全平方数和的最小数量。- 状态转移方程:
- 对于每个
i
,我们可以尝试用一个平方数j^2
来减少i
,即:
d p [ i ] = min ( d p [ i ] , d p [ i − j 2 ] + 1 ) dp[i] = \min(dp[i], dp[i - j^2] + 1) dp[i]=min(dp[i],dp[i−j2]+1) - 其中,
j
是满足j^2 <= i
的所有平方数。
- 对于每个
状态转移思路:
- 对于每个整数
i
,我们可以枚举所有小于i
的平方数j^2
,并通过递归减少问题的规模。例如:
d p [ i ] = min ( d p [ i − 1 2 ] + 1 , d p [ i − 2 2 ] + 1 , d p [ i − 3 2 ] + 1 , … ) dp[i] = \min(dp[i - 1^2] + 1, dp[i - 2^2] + 1, dp[i - 3^2] + 1, \dots) dp[i]=min(dp[i−12]+1,dp[i−22]+1,dp[i−32]+1,…)
边界条件:
dp[0] = 0
:数字0
可以表示为 0 个完全平方数。
3. 解决方法
动态规划方法
- 初始化:创建一个数组
dp
,其中dp[i]
表示数字i
的最少完全平方数表示数量。 - 状态转移:对于每个数字
i
,枚举小于i
的所有平方数,并根据转移方程更新dp[i]
。 - 返回结果:最终
dp[n]
即为数字n
的最少完全平方数表示数量。
伪代码:
initialize dp array with dp[0] = 0
for i from 1 to n:
for j from 1 to sqrt(i):
dp[i] = min(dp[i], dp[i - j^2] + 1)
return dp[n]
4. 进一步优化
- 时间复杂度:时间复杂度为
O(n * sqrt(n))
,因为对于每个i
,我们要枚举小于i
的平方数。 - 空间复杂度:空间复杂度为
O(n)
,因为我们需要存储数组dp
。
5. 小总结
- 递推思路:通过动态规划,从小到大构建
dp
数组,每个i
的值可以通过前面的平方数和的最优解来确定。 - 时间复杂度:时间复杂度为
O(n * sqrt(n))
,空间复杂度为O(n)
,适合处理中等规模的输入。
以上就是完全平方数问题的基本思路。
Python代码
class Solution:
def numSquares(self, n: int) -> int:
# 初始化dp数组,dp[i]表示数字i的最少完全平方数数量
dp = [float('inf')] * (n + 1)
dp[0] = 0 # 数字0可以表示为0个完全平方数
# 动态规划计算
for i in range(1, n + 1):
# 枚举所有小于i的平方数
for j in range(1, int(math.sqrt(i)) + 1):
dp[i] = min(dp[i], dp[i - j * j] + 1)
return dp[n] # 返回数字n的最少完全平方数数量
Python代码解释
- 初始化:定义一个
dp
数组,dp[i]
表示数字i
的最少完全平方数数量,初始值设为正无穷,dp[0] = 0
。 - 动态规划递推:通过枚举小于
i
的所有平方数,更新dp[i]
。 - 返回结果:最终
dp[n]
即为最少完全平方数的数量。
C++代码
class Solution {
public:
int numSquares(int n) {
// 初始化dp数组,dp[i]表示数字i的最少完全平方数数量
vector<int> dp(n + 1, INT_MAX);
dp[0] = 0; // 数字0可以表示为0个完全平方数
// 动态规划计算
for (int i = 1; i <= n; ++i) {
// 枚举所有小于i的平方数
for (int j = 1; j * j <= i; ++j) {
dp[i] = min(dp[i], dp[i - j * j] + 1);
}
}
return dp[n]; // 返回数字n的最少完全平方数数量
}
};
C++代码解释
- 初始化:定义
dp
数组,dp[i]
表示数字i
的最少完全平方数数量,初始值为INT_MAX
,dp[0] = 0
。 - 动态规划递推:通过枚举每个数字
i
,并计算小于i
的所有平方数的组合,更新dp[i]
。 - 返回结果:最终返回
dp[n]
,即最少的完全平方数数量。
总结
- 核心思路:利用动态规划,自底向上构建
dp
数组,逐步求解每个数字i
的最少完全平方数表示。 - 时间复杂度:时间复杂度为
O(n * sqrt(n))
,适合处理中等规模的n
。 - 空间复杂度:空间复杂度为
O(n)
,因为我们需要维护一个大小为n+1
的数组。