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

Leetcode-100 完全平方数

和为n的完全平方数

解题思路

本问题可以采用**记忆化搜索(DFS + Memoization)**的方法解决,核心思路是通过递归遍历所有可能的平方数组合,利用缓存避免重复计算。关键点在于将问题分解为子问题:对于每个可能的平方数i²,选择或不选择该数,并递归求解剩余部分。

算法步骤

  1. 问题分解:将问题转化为在平方数列表中选择若干数,使其和为n,求最少选择个数。平方数列表为[1,4,9,…,k²],其中k = isqrt(n)
  2. 递归定义:定义dfs(i, j)表示用前i个平方数(即1²,2²,…,i²)组成和j的最少数量
  3. 递归逻辑
    • 终止条件:当i=0时,只有j=0时有解(返回0),否则无解(返回无穷大)
    • 剪枝优化:当当前平方数i² > j时,只能不选i²
    • 状态转移:比较选择和不选择i²的情况,取最小值
  4. 记忆化优化:通过@cache缓存中间结果,避免重复计算

具体例子分析

以n=12为例:

  1. 最大平方数为3²=9,初始调用dfs(3, 12)
  2. 递归树展开:
    • 选择3²:剩余12-9=3 → dfs(3,3)+1
    • 不选3²:继续考虑2² → dfs(2,12)
  3. 最终最优解为3(4+4+4),递归路径如下:
    • dfs(3,12) → dfs(3,3)+1 → dfs(2,3) → dfs(1,3) → dfs(1,2)+1 → dfs(1,1)+1 → dfs(1,0)+1 → 0

代码实现

from math import isqrt
from functools import cache

class Solution:
 def numSquares(self, n: int) -> int:
     @cache
     def dfs(i: int, j: int) -> int:
         if i == 0:
             return float('inf') if j != 0 else 0
         if j < i*i:
             return dfs(i-1, j)
         return min(dfs(i-1, j), dfs(i, j - i*i) + 1)
     
     return dfs(isqrt(n), n)

复杂度分析

  • 时间复杂度:O(n * √n)
    每个状态 (i, j) 最多计算一次,其中:

    • i 的范围是 [1, √n]
    • j 的范围是 [0, n]
    • 总状态数为 √n * n
      由于每个状态转移需要 O(1) 时间,总体时间复杂度为 O(n * √n)
  • 空间复杂度:O(n * √n)
    由于使用 记忆化缓存 存储所有可能的状态,需要 O(n * √n) 的空间。

优化

解题思路

本方法通过预计算动态规划表的方式,预先计算出每个数的最小平方数组合,从而在查询时达到O(1)时间复杂度。核心思想是将完全平方数问题转化为完全背包问题,通过动态规划求解。

算法步骤

  1. 预计算阶段

    • 定义最大计算范围N=10000
    • 初始化动态规划数组f,其中f[0]=0,其余初始化为无穷大
    • 遍历所有可能的平方数(i从1到√N)
    • 对每个平方数,更新所有可能的值j(从i²到N)
  2. 状态转移方程

    f[j] = \min(f[j],\ f[j-i^2] + 1)
    

表示对于数值j,可以通过选择当前平方数i²来优化结果

  1. 查询阶段
  • 直接返回预计算结果f[n]

具体例子

n = 12 为例,预计算过程如下:

当前平方数更新过程f[12] 变化
$begin:math:text 1 2 = 1 1^2=1 12=1end:math:text$f[12] = min(∞, f[11] + 1) → 1212 (1+1+…+1)
$begin:math:text 2 2 = 4 2^2=4 22=4end:math:text$f[12] = min(12, f[8] + 1 = 3)3 (4+4+4)
$begin:math:text 3 2 = 9 3^2=9 32=9end:math:text$f[12] = min(3, f[3] + 1 = 4)保持 3

最终结果:
b e g i n : m a t h : d i s p l a y begin:math:display begin:math:display
f[12] = 3
e n d : m a t h : d i s p l a y end:math:display end:math:display
12 可由 3 个完全平方数之和构成,如 4 + 4 + 4

from math import isqrt
import sys

# 预计算到10000的所有可能值
MAX_N = 10000
f = [0] + [sys.maxsize] * MAX_N  # 初始化:f[0]=0,其他为无穷大

# 动态规划填表
for i in range(1, isqrt(MAX_N) + 1):
    square = i * i
    for j in range(square, MAX_N + 1):
        if f[j - square] + 1 < f[j]:
            f[j] = f[j - square] + 1

class Solution:
    def numSquares(self, n: int) -> int:
        return f[n] if n <= MAX_N else -1  # 处理超出预计算范围的情况

复杂度分析

  • 预计算时间复杂度: O ( N N ) O(N\sqrt{N}) O(NN )

    • 外层循环 执行 N \sqrt{N} N (遍历所有可能的完全平方数)。
    • 内层循环 执行 N N N(遍历 0 0 0 N N N 进行状态转移)。
    • 总体复杂度为 O ( N N ) O(N\sqrt{N}) O(NN )
  • 查询时间复杂度: O ( 1 ) O(1) O(1)

    • 直接访问 预计算结果,查询时间为 O ( 1 ) O(1) O(1)
  • 空间复杂度: O ( N ) O(N) O(N)

    • 需要存储 长度为 N + 1 N+1 N+1 的 DP 数组,空间复杂度为 O ( N ) O(N) O(N)

相关文章:

  • Python——无损将 .mp4 转成 .gif
  • vue前端杂记
  • 亚马逊云科技Marketplace(中国区)上架专业服务产品, “云生态连接器”价值凸显
  • flask 安装后不能识别
  • 系统架构设计师-第3章 数据库设计
  • 【mysql】有索引和没有索引字段更新时锁的不同
  • 使用Spring AI调用AI模型
  • YOLOv8改进SPFF-LSKA大核可分离核注意力机制
  • docker oracle11
  • windows2012部署redis5并安装为服务
  • svn 通过127.0.01能访问 但通过公网IP不能访问,这是什么原因?
  • Nacos简介、安装与使用(保姆级教程!!!)
  • ProfibusDP主站转ModbusTCP网关如何进行数据互换
  • 学习日记-250305
  • 大数据环境(单机版) Flume传输数据到Kafka
  • C++ JSON库的使用示例
  • MambaMorph:基于 Mamba 的医学 MR-CT 可变形配准框架
  • scBaseCamp:一个AI代理的可持续扩充的单细胞数据存储库
  • Huggingface transformers库使用教程(翻译)--------(2)使用piplines进行推理
  • IDC权威认证!永洪科技入选 IDC「GBI图谱」,点亮生成式 BI 价值灯塔
  • 力杨网站建设/seo建设招商
  • 武汉网站推广优化公司/培训机构需要什么资质
  • 网站免费源码大全无用下载/电商运营主要负责什么
  • 北京网站建设比较好的公司/长沙seo全网营销
  • wordpress4.9.4 mysql/优化科技
  • 用spl做网站/网络推广有效果吗