给你一个整数
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
思路:
- 找到 n 之前最大的一个完全平方数 j(j*j<=i),记为一个个数;那么 还剩 n-j*j 需要继续拼凑。也就是说只要将 n-j*j 的解 dp[n-j*j] 加上上面 j*j 所占的那个1,就是最终解,也是最少的。
- 注意:dp数组初始化时,赋值只要是 比n大的任意数 都可以,比如 dp[i] = n+1 或 dp[i]=math.MaxInt32 ...
因为最终的dp方程 dp[i] = min(dp[i], dp[i-j*j] + 1) 中是要求min较小值。
其中 dp[i-j*j] + 1 只有跟一个 比n大的任意数dp[i] 相比,才能保证 dp[i-j*j] + 1 的值相对较小,从而获取到较小值:dp[i-j*j] + 1 - 背包问题 更多可参考 LeetCode题解
时间复杂度:
O(n√n),其中 n 为给定的正整数。外循环时间复杂度为O(n),内循环的时间复杂度为 O(√n),因此总时间复杂度为 O(n√n)。
空间复杂度:
O(n),dp数组所需。
// 理解:找到n之前最大的一个完全平方数j(j*j<=n),记为一个个数;那么 还剩n-j*j需要继续拼凑。也就是说只要将n-j*j的解dp[n-j*j] 加上上面j*j所占的那个1,就是n的解,这就是最短的。
func numSquares(n int) int {
dp := make([]int, n+1) // 下标从0开始
dp[0] = 0 // 题目要求从1的平方开始,所以0的情况可忽略
// 初始化dp数组,此处注释,因为此步骤可以合并到下面的for循环中
// for i := 1; i <= n; i++ {
// dp[i] = n+1 // dp[i]一开始全部都初始化为了n+1,比要求的n还大
// }
for i := 1; i <= n; i++ { // 当前循环中,需要组合的完全平方数为n
dp[i] = n+1
for j := 1; j*j <= i; j++ {
// j从1开始,j*j不断增大,因此 diff=i-j*j 最终可以保证差值最小
// +1:且j*j本身也是组成目标和n的其中一个完全平方数。
// 比如:dp[13]=min(dp[13], dp[13-1*1] + 1), dp[13]=min(dp[13], dp[13-2*2] + 1), dp[13]=min(dp[13], dp[13-3*3] + 1)...
dp[i] = min(dp[i], dp[i-j*j] + 1)
}
}
return dp[n]
}
func min(a, b int) int {
if a < b {
return a
}
return b
}