【算法题】279. 完全平方数-力扣(LeetCode)
1.题目
下方是力扣官方题目的地址
给你一个整数 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
2.题解
思路
这是一道典型的动态规划中的完全背包题
完全背包示例:
背包容量 c=13
物品重量 w=[1,2,3,4,5]
所对应的价值 v=[2,3,4,5,8]
完全背包里面的物品可以无限选
求如何选价值最大
这当然是都选第一个,价值:26
我们将这题类比于完全背包
我们以n=12
为例:
里面的n
就是完全背包中的c(背包容量)
价值是什么呢?
我们要求最小数量,而每家一个物品,数量就加一,所以这里面 的价值v
恒等于1
,即:
v=1
这题要求求最小价值
那么物品重量是什么呢?
当然就是小于等于n
的完全平方数了,即:
w=[1,2,4,9]
把这题转换成完全背包后,我们直接套模板就行了
直观Python代码
class Solution(object):
def numSquares(self, n):
"""
:type n: int
:rtype: int
"""
ans=1
w=[]
v=1
while ans**2<=n:
w.append(ans**2) # 生成完全平方数
ans+=1
dp=[[float('inf')]*(n+1) for _ in range(len(w)+1)] # 初始化dp数组
for i in range(len(w)+1): # 预处理一些已知的值
dp[i][0]=0
for i in range(1,len(w)+1):
for j in range(1,n+1): # 套用模板
dp[i][j]=dp[i-1][j]
if j>=w[i-1]:dp[i][j]=min(dp[i][j],dp[i][j-w[i-1]]+v)
return dp[-1][-1]
以上代码较为直观,但是比较复杂,我们可以对其进行优化:
优化思路
- 空间优化:由于动态规划的状态转移方程只依赖于前一行的数据,可以将二维数组优化为一维数组。
- 减少不必要的计算:在构建完全平方数列表时,可以直接使用一个列表来存储这些数,而不需要每次计算平方。
优化后的Python代码
class Solution(object):
def numSquares(self, n):
"""
:type n: int
:rtype: int
"""
# 生成所有小于等于 n 的完全平方数
w = [i * i for i in range(1, int(n**0.5) + 1)]
v=1
# 初始化一维动态规划数组
dp = [float('inf')] * (n + 1)
dp[0] = 0
# 动态规划填表
for i in w:
for j in range(i, n + 1):
dp[j] = min(dp[j], dp[j - i] + v)
return dp[n]
优化点解释
- 生成完全平方数:
- 使用列表推导式
w = [i * i for i in range(1, int(n**0.5) + 1)]
直接生成所有小于等于n
的完全平方数。 - 这样可以避免在每次循环中计算平方,提高效率。
- 使用列表推导式
- 一维动态规划数组:
- 将二维数组
dp
优化为一维数组dp
,只保留当前行的状态。 - 初始化
dp
数组,dp[0]
设为 0,表示组成 0 的最少完全平方数为 0。 - 遍历每个完全平方数
i
,更新dp
数组中的值。
- 将二维数组
- 状态转移方程:
- 对于每个完全平方数
i
,更新dp[j]
的值,其中j
从i
到n
。 - 状态转移方程为
dp[j] = min(dp[j], dp[j - i] + v)
,表示当前值j
可以通过减去一个完全平方数i
并加上v
来得到。
- 对于每个完全平方数
3.结语
本人资历尚浅,发博客主要是记录与学习,欢迎大佬们批评指教!大家也可以在评论区多多交流,相互学习,共同成长。