Leetcode: 完全平方数

90 阅读3分钟

原题:leetcode.cn/problems/pe…

题目描述

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

完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,149 和 16 都是完全平方数,而 3 和 11 不是。

解题思路

  1. 动态规划
    • 定义 dp[i] 表示和为 i 的最少完全平方数个数
    • 初始化 dp = 0(边界条件)
    • 状态转移:dp[i] = min(dp[i], dp[i - j*j] + 1) (j 为平方数,1 ≤ j ≤ √i
  2. 数学优化
    • 基于四平方和定理:任何正整数可表示为4个平方数和
    • 特殊处理:若 n 是完全平方数,直接返回1
  3. BFS剪枝
    • 将问题转化为层级遍历(0→n的最短路径)
    • 每层减去平方数,首次到达0的层级即解

关键洞察

  1. 子问题重叠
    • 大数解依赖小数解(如 dp=dp+1=dp+3
    • 动态规划自然复用子问题解
  2. 平方数边界
    • 只需考虑 j*j ≤ i 的平方数
    • 内层循环可优化为 j=1→√i
  3. 四平方和定理
    • 解仅可能为1/2/3/4四种情况
    • 若 n=4^k*(8m+7) 则解为4
  4. BFS层级特性
    • 每层表示当前剩余数值
    • 减平方数操作等价于边权为1的图搜索

复杂度分析

方法时间复杂度空间复杂度
动态规划O(n√n)O(n)
BFSO(n)(最坏情况)O(n)
数学法O(√n)O(1)

:n为输入正整数,√n表示平方根取整

代码实现

  • 动态规划
var numSquares = function (n) {
    // 创建动态规划数组,dp[i] 表示和为 i 的最少完全平方数数量
    const dp = new Array(n + 1).fill(Infinity);
    dp[0] = 0; // 基础情况:和为 0 需要 0 个平方数
    // 预先计算所有可能用到的平方数
    const squares = [];
    for (let i = 1; i * i <= n; i++) {
        squares.push(i * i);
    }
    // 动态规划填充过程
    for (let i = 1; i <= n; i++) {
        for (const square of squares) {
            if (square > i) break; // 平方数大于当前值,提前终止
            dp[i] = Math.min(dp[i], dp[i - square] + 1);
        }
    }
    return dp[n];
};
  • BFS
/**
 * @param {number} n
 * @return {number}
 */
const numSquares = (n) => {
    const queue = [n];
    const visited = new Set([n]);
    let level = 0;

    while (queue.length) {
        level++;
        const size = queue.length;
        for (let i = 0; i < size; i++) {
            const cur = queue.shift();
            for (let j = 1; j * j <= cur; j++) {
                const next = cur - j * j;
                if (next === 0) return level;
                if (!visited.has(next)) {
                    visited.add(next);
                    queue.push(next);
                }
            }
        }
    }
    return level;
};
  • 数学法
/**
 * @param {number} n
 * @return {number}
 */
const numSquares = (n) => {
    // 检测是否为完全平方数
    const isSquare = (x) => Math.floor(Math.sqrt(x)) ** 2 === x;

    // 情况1:n为完全平方数
    if (isSquare(n)) return 1;

    // 情况2:检查是否存在 a^2+b^2=n
    for (let i = 1; i * i <= n; i++) {
        if (isSquare(n - i * i)) return 2;
    }

    // 情况3:四平方和定理特殊情况
    while (n % 4 === 0) n /= 4;
    if (n % 8 === 7) return 4;

    // 其他情况为3
    return 3;
};

实际应用场景

  1. 图像压缩:将图像块表示为平方和优化存储`
  2. 密码学:有限域平方和分解`
  3. 物理建模:能量量子化表示`
  4. 金融拆分:资产单位最小化分割`

相似题目