【力扣100题】44.完全平方数

题目描述

给定一个整数 n,返回和为 n 的完全平方数的最少数量

完全平方数是一个整数,其值等于另一个整数的平方。例如 1、4、9、16 都是完全平方数,而 311 不是。

示例:

  • 输入:n = 12 → 输出:312 = 4 + 4 + 4
  • 输入:n = 13 → 输出:213 = 4 + 9

解题思路总览

方法 思路 时间复杂度 空间复杂度
动态规划 dp[i] 表示和为 i 的最少完全平方数个数,状态转移 dp[i] = min(dp[i], dp[i-j*j]+1) O(n√n) O(n)
数学公式 勒让德三平方定理,判断是否能表示为 1/2/3/4 个平方数之和 O(1) O(1)

本题采用动态规划方法。


完整代码

cpp 复制代码
class Solution {
public:
    int numSquares(int n) {
        vector<int> dp(n + 1, INT_MAX);
        dp[0] = 0;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j * j <= i; j++) {
                dp[i] = min(dp[i - j * j] + 1, dp[i]);
            }
        }
        return dp[n];
    }
};

算法流程图

复制代码
输入: n = 12

初始化:
  dp[0] = 0
  dp[1...12] = INT_MAX

i = 1:
  j = 1, j*j = 1 <= 1
    dp[1] = min(INT_MAX, dp[0]+1) = 1
  dp[1] = 1

i = 2:
  j = 1, j*j = 1 <= 2
    dp[2] = min(INT_MAX, dp[1]+1) = 2
  j = 2, j*j = 4 > 2, 退出
  dp[2] = 2

i = 3:
  j = 1, j*j = 1 <= 3
    dp[3] = min(INT_MAX, dp[2]+1) = 3
  j = 2, j*j = 4 > 3, 退出
  dp[3] = 3

i = 4:
  j = 1, j*j = 1 <= 4
    dp[4] = min(INT_MAX, dp[3]+1) = 4
  j = 2, j*j = 4 <= 4
    dp[4] = min(4, dp[0]+1) = 1
  dp[4] = 1

... (继续迭代直到 i = 12)

i = 12:
  j = 1: dp[12] = dp[11]+1 = 4
  j = 2: dp[12] = dp[8]+1 = 3
  j = 3: dp[12] = dp[3]+1 = min(3, 4) = 3
  j = 4: dp[12] = dp[-4]无效, 跳过 (j*j=16>12)

最终 dp[12] = 3
输出: 3

逐行解析

cpp 复制代码
vector<int> dp(n + 1, INT_MAX);

含义: 创建大小为 n+1 的数组,dp[i] 表示和为 i 的完全平方数的最少数量。初始化为 INT_MAX 表示尚未计算。

cpp 复制代码
dp[0] = 0;

含义: 基础情况,和为 0 需要 0 个完全平方数。

cpp 复制代码
for (int i = 1; i <= n; i++)

含义: 从 1 到 n 依次计算每个数的最少完全平方数个数。

cpp 复制代码
for (int j = 1; j * j <= i; j++)

含义: 枚举所有可能的完全平方数 j*j,其中 j 从 1 开始,直到 j*j 不超过 i

cpp 复制代码
dp[i] = min(dp[i], dp[i - j * j] + 1);

含义: 状态转移方程。如果从 i 中减去一个完全平方数 j*j,那么剩下的 i-j*j 需要 dp[i-j*j] 个平方数,再加上当前的 j*j,总共 dp[i-j*j]+1 个。取较小值更新 dp[i]


复杂度分析

复杂度 说明
时间复杂度 O(n√n) 外层循环 n 次,内层循环最多 √n 次(因为 j*j ≤ i)
空间复杂度 O(n) 需要大小为 n+1 的 dp 数组

面试追问 FAQ

问题 答案
为什么 dp[0] = 0 0 的完全平方数之和只能是 0,需要 0 个数字,这是动态规划的基础情况
能否用 BFS 解决? 可以,将 n 作为起点,每次减去一个完全平方数,层数即为最少数量
如何优化到 O(n)? 使用数学方法:勒让德三平方定理可 O(1) 判断,但实现复杂
完全平方数有哪些性质? 平方数 mod 4 结果为 0 或 1,可用于快速判断某个数是否为完全平方数
为什么内层循环是 j*j <= i 因为 j*j 作为被减数不能超过 i,否则 i-j*j 为负数

相关题目

题号 题目 难度 核心思路
279 完全平方数 中等 动态规划/BFS
322 零钱兑换 中等 完全背包
300 最长递增子序列 中等 动态规划
139 单词拆分 中等 完全背包

总结

要点 内容
核心思想 动态规划,枚举所有完全平方数作为最后一个加数
状态定义 dp[i] = 和为 i 的最少完全平方数个数
状态转移 dp[i] = min(dp[i], dp[i-j*j]+1)
边界条件 dp[0] = 0

相关推荐
橙淮1 小时前
哈希核心:高效映射与安全加密
算法·哈希算法
浅念-9 小时前
递归解题指南:LeetCode经典题全解析
数据结构·算法·leetcode·职场和发展·排序算法·深度优先·递归
Kiling_070410 小时前
Java集合进阶:Set与Collections详解
算法·哈希算法
智者知已应修善业10 小时前
【51单片机89C51及74LS273、74LS244组成】2022-5-28
c++·经验分享·笔记·算法·51单片机
洛水水10 小时前
【力扣100题】33.验证二叉搜索树
算法·leetcode·职场和发展
SimpleLearingAI11 小时前
聚类算法详解
算法·数据挖掘·聚类
刀法如飞12 小时前
Go 字符串查找的 20 种实现方式,用不同思路解决问题
算法·面试·程序员
Dlrb121113 小时前
C语言-指针数组与数组指针
c语言·数据结构·算法·指针·数组指针·指针数组·二级指针
WL_Aurora13 小时前
Python 算法基础篇之集合
python·算法