LeetCode-279:完全平方数,用“最短路思维”写出最稳的 DP
本题在线练习:LeetCode 279. 完全平方数 - 在线练习(免费 · 无需登录 · AI 辅助)(https://onefly.top/zero2Leetcode/playground.html?id=279)
配套刷题网站 Zero2Leetcode - 内置本地 OJ + AI 教练,零门槛开刷 Hot 100。
题目概述
给定一个正整数 n,要求用若干个完全平方数(如 1, 4, 9, 16, ...)相加得到 n,并返回所需的最少平方数个数。
例如:
n = 12,可以是4 + 4 + 4,最少是3n = 13,可以是4 + 9,最少是2
核心思路:把它当作“最少步数”的 DP
这题问的是“最少个数”,很像最短路/最少步数:
从 0 走到 n,每一步可以加上一个平方数 s,代价是 +1 步。最少步数就是答案。
用 DP 表达就是:
dp[x]:凑出和为x的最少平方数个数
初始化:
dp[0] = 0- 其他先设成一个很大的数
转移:
对每个 x,尝试最后一步选择一个平方数 s = k*k(且 s <= x):
dp[x] = min(dp[x], dp[x - s] + 1)
代码实现(经典一维 DP)
class Solution:
def numSquares(self, n: int) -> int:
squares = []
k = 1
while k * k <= n:
squares.append(k * k)
k += 1
INF = 10**9
dp = [INF] * (n + 1)
dp[0] = 0
for x in range(1, n + 1):
for s in squares:
if s > x:
break
dp[x] = min(dp[x], dp[x - s] + 1)
return dp[n]
逐行拆解
- 先把
<= n的平方数列出来,这样内层循环不需要每次重新算平方。 dp[x]表示“最少用几块砖(平方数)拼出长度 x”。- 计算
dp[x]时,枚举最后放的那块砖s,剩下的长度是x - s:- 如果
dp[x - s]已经是最少块数,那么dp[x - s] + 1是一种可行拼法 - 对所有
s取最小即可
- 如果
手动模拟(n = 12)
平方数集合是 [1, 4, 9]。
dp[1] = dp[0] + 1 = 1dp[4] = min(dp[3]+1, dp[0]+1) = 1(直接用 4)dp[8] = min(dp[7]+1, dp[4]+1) = 2(4+4)dp[12] = min(dp[11]+1, dp[8]+1, dp[3]+1) = 3(4+4+4)
复杂度分析
设 m = floor(sqrt(n)),平方数个数是 m:
- 时间复杂度:
O(n * sqrt(n)) - 空间复杂度:
O(n)
总结
这题最重要的是抓住“最少个数”这个关键词,然后把问题固定成一个标准模型:
dp[x]= 凑到x的最少步数,最后一步枚举一个平方数。
一旦写出 dp[x] = min(dp[x - k^2] + 1),实现就非常直接,而且这一套思路还能复用到很多“最少硬币/最少步数”的题里。

浙公网安备 33010602011771号