题目描述
给定一个整数 n ,返回 和为 n 的完全平方数的最少数量 。
完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。
例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。
示例 :
输入:n = 12
输出:3
解释:12 = 4 + 4 + 4
这道题和硬币问题很相似,是一道典型的动态问题。
题目类型
当遇到下列问题,且问题可以分解成子问题来解决,考虑用动态规划来解决。
- 求最大值或最小值
- 求可不可行
- 求方案总数
解题思路
解动态规划题,最重要的是列出动态转移方程。考虑子问题,如果和为 0 ~ i -1(0,1,2,…,i,i-1)完全平方数的最少数量存于dp中,那么对于当前数字 i,dp[ i ] = min(dp[ i ] , dp[ i - j * j ] + 1 ),对所有 i - j*j >= 0, dp[i] 最多可以由i个1组成,初始化dp[i] = 1
例如,dp[5] = min(dp[5]=5,dp[5-1]+1,dp[5-4]+1)
具体步骤,
-
首先初始化长度为 n+1 的数组 dp,每个位置都为 0,如果 n 为 0,则结果为 0
-
对数组进行遍历,下标为 i,每次都将当前数字先更新为最大的结果,即 dp[i]=i, i = 1时,要求 i - j * j >= 0, j 只能取1
dp[1] = 1
dp[i - j* j] + 1 = dp[1-1*1] + 1 = dp[0] + 1 = 1
因此dp[1] = 1
-
下一步 i = 2,初始化dp[2] = 2, 要求 i - j * j >= 0, j 也是只能取1
dp[2] = 2
dp[i - j* j] + 1= dp[2 - 1*1] + 1= dp[1] + 1 = 1+1 = 2
所以最后dp[2] = 2
-
下一步 i = 3,初始化dp[2] = 3, 要求 i - j * j >= 0, j 也是只能取1,
dp[3] = 3
dp[i - j* j] + 1= dp[3 - 1*1] + 1= dp[2] + 1= 2+1 = 3
所以最后dp[3] = 3
-
下一步 i = 4,初始化dp[2] = 3, 要求 i - j * j >= 0, j 能取1,2
j = 1,
dp[3] = 3
dp[i - j * j ] + 1 = dp[ 3-11] + 1= dp[2] + 1= 3
此时,dp[4] 更新为 3
j = 2
dp[4] = 3
dp[i - j * j ] + 1 = dp[ 4-22] + 1= dp[0] + 1= 1
因此 dp[4] = 1
-
i = 5, 同理,j = {1,2}
代码
class Solution:
def numSquares(self, n: int) -> int:
dp = [0]*(n+1)
for i in range(1,n+1):
dp[i] = i
for j in [k for k in range(1,i) if k*k <= i]:
dp[i] = min(dp[i],dp[i-j*j]+1)
return dp[-1]