当前位置: 首页 > news >正文

算法279. 完全平方数

题目

给你一个整数 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

题解

# 写在外面,多个测试数据之间可以共享,减少计算量
@cache  # 缓存装饰器,避免重复计算 dfs 的结果(记忆化)
def dfs(i: int, j: int) -> int:if i == 0:return inf if j else 0if j < i * i:return dfs(i - 1, j)  # 只能不选return min(dfs(i - 1, j), dfs(i, j - i * i) + 1)  # 不选 vs 选class Solution:def numSquares(self, n: int) -> int:return dfs(isqrt(n), n)

代替@cache的题解

# 手动创建缓存字典
memo = {}
def dfs(i: int, j: int) -> int:# 1. 先查缓存if (i, j) in memo:return memo[(i, j)]# 2. 边界条件if i == 0:res = float('inf') if j else 0elif j < i * i:res = dfs(i - 1, j)else:res = min(dfs(i - 1, j), dfs(i, j - i * i) + 1)# 3. 存入缓存memo[(i, j)] = resreturn resclass Solution:def numSquares(self, n: int) -> int:return dfs(int(n**0.5), n)

解题思路

🎯 问题到底是什么?

题目:给一个正整数 n,问最少用多少个完全平方数(1, 4, 9, 16, 25…)加起来等于 n

比如:

  • n = 12
    可能的组合:
    • 4 + 4 + 4 → 用了 3 个
    • 9 + 1 + 1 + 1 → 用了 4 个
    • 1+1+…+1(12个1)→ 12 个
      ✅ 最少是 3 个

我们的目标:找到这个“最少个数”


🤔 人类怎么思考这个问题?

假设 n = 12,你会怎么想?

  1. 先看最大的平方数 ≤12 是多少?→ 9(因为 16>12)
  2. 然后考虑两种可能:
    • 不用 9:那就在更小的平方数(1, 4)里凑 12
    • 用 9:那剩下 12 - 9 = 3,再用平方数凑 3(注意:还能继续用 9 吗?不能,因为 9>3,但以后如果剩下够大,是可以重复用的!)

🔁 关键:平方数可以重复用(比如 4+4+4),所以这是一个“可以回头再选”的问题。


🧠 代码的核心思想(用人话)

定义一个函数:

dfs(最大可用的平方根, 要凑的目标数) → 返回最少需要几个平方数

比如:

  • dfs(3, 12) 表示:“用 1², 2², 3²(即 1,4,9)来凑 12,最少要几个?”

那怎么算 dfs(3, 12)

  • 选项1:不用 3²(即不用9)
    → 问题变成:用 1,4 凑 12 → dfs(2, 12)
  • 选项2:用一个 3²(即用一个9)
    → 剩下 12-9=3,还要凑 3,而且还能继续用 9(虽然现在用不上,但函数设计要通用)→ dfs(3, 3) + 1(+1 是因为用了一个9)

然后选这两个选项中更小的那个

min( dfs(2,12), dfs(3,3)+1 )

🔁 递归是怎么工作的?(以 n=12 为例)

我们画个简化版的递归树:

dfs(3,12)
├─ 不用9 → dfs(2,12)
│   ├─ 不用4 → dfs(1,12) → 只能用1 → 需要12个
│   └─ 用4 → dfs(2,8)+1
│       ├─ 用4 → dfs(2,4)+1
│       │   └─ 用4 → dfs(2,0)+1 = 0+1=1 → 所以 dfs(2,4)=1
│       └─ 所以 dfs(2,8) = min(..., 1+1=2) → 最终 dfs(2,12)=3
└─ 用9 → dfs(3,3)+1└─ 3<9 → 只能用更小的 → dfs(2,3)└─ 3<4 → 只能用1 → dfs(1,3)=3→ 所以 dfs(3,3)+1 = 3+1 = 4

最终:min(3, 4) = 3


❓ 代码里的 i == 0 是什么意思?

  • i 是“最大可用的平方根”,比如 i=3 对应最大用 9。
  • i=0,意味着“最大只能用 0²=0”。
    • 如果目标 j=0:什么都不用,0 个 → 返回 0
    • 如果目标 j>0:只能用 0 凑正数,不可能 → 返回一个很大的数(inf),这样在 min() 里会被忽略

❓ 为什么用 @cache

因为递归过程中会重复计算相同的问题
比如 dfs(2,4) 可能在多个分支中被计算多次。

@cache 会记住 (i, j) 的结果,下次直接返回,避免重复劳动


❓ 为什么初始调用是 dfs(isqrt(n), n)

  • isqrt(n)n整数平方根
    • 比如 n=12isqrt(12)=3,因为 3²=9 ≤12,而 4²=16>12
  • 所以最大的有用平方数就是 3²=9,没必要考虑更大的(比如16,根本用不上)

🧩 一句话总结代码逻辑

从最大的平方数开始,对每个数决定“用”还是“不用”,递归求解剩下的目标,最后选个数最少的方案。用缓存避免重复计算。


💡 如果还是抽象,记住这个模板

这类“最少多少个数凑成目标”的问题,通常可以这样想:

最少个数(目标) = min(最少个数(目标 - 选一个a) + 1,最少个数(目标 - 选一个b) + 1,...
)

而这段代码只是用“从大到小枚举平方数”的方式实现了这个思想。


总结

这是一个“完全背包”的问题,关于解答此类问题的解法“完全背包”和“0-1背包”,见另一篇文章。
【动态规划】题目中的「0-1 背包」和「完全背包」的问题

http://www.dtcms.com/a/481844.html

相关文章:

  • Prometheus pushgateway学习
  • MySQL索引结构:B树与B+树
  • 进程的基本认识
  • Webpack 打包优化与骨架屏结合:双管齐下提升前端性能与用户体验
  • 鸿蒙:在沙箱目录下压缩或解压文件
  • 智能SQL客户端Chat2DB技术解析
  • 电影网站推广什么是网络营销的主要职能之一
  • Transformers库用法示例:解锁预训练模型的强大能力
  • 大气污染扩散calpuff模型:数据预处理、Calmet气象模块、Post Tools 后处理工具及绘图工具
  • 用气安全与能效优化平台
  • 02117 信息组织【第三章】
  • 自己建设淘宝客网站需要备案么wordpress插件 投票
  • Wireshark 4.4.9 设置为中文界面方法
  • 极限AI Coding,腾讯云“黑客松”大赛回顾(内有作品开源)
  • 【工具分享】Dota游戏平台助手
  • 网站制作找云优化口碑好的网站定制公司
  • 精品建站公司2345网址大全下载到桌面
  • HENGSHI SENSE异构过滤架构:基于三层执行引擎的跨源联邦查询性能优化实践
  • 语言模型监督式微调(SFT)概述
  • 又开始了 小程序定制
  • 前端面试-箭头函数
  • 翻译类公司网站模板node做网站后台
  • 2018做网站哪里可以做寄生虫网站
  • 腾讯云的游戏盾怎么样
  • C++函数完全指南:从基础到高级应用
  • 国自然申报·医工交叉热点|单细胞多模态融合破解病理研究痛点
  • html情人节给女朋友做网站WordPress在手机能更新
  • springboot餐厅信息管理系统设计(代码+数据库+LW)
  • Jenkins Share Library教程 —— 高级实战与最佳实践教程
  • Blender图片AI智能一键生成3D模型插件 Pixelmodeller Ai V1.4.9