【LeetCode】动态规划—279. 完全平方数(附完整Python/C++代码)

题目描述

在这里插入图片描述

前言

完全平方数问题 是一个经典的动态规划问题。给定一个正整数 n,要求将其表示为若干个完全平方数的和,且要求这些完全平方数的数量最少。例如:12 = 4 + 4 + 4,因此 12 可以表示为 3 个完全平方数的和。

我们可以利用动态规划的思想,逐步构建出最优解,即找到使得 n 表示为最少的完全平方数的方案。


基本思路

1. 问题定义

给定一个正整数 n,要求将其分解为若干个完全平方数的和,并且完全平方数的数量最少。输出最少的完全平方数数量。

举例:

  • 输入:n = 12

  • 输出:3,因为 12 = 4 + 4 + 4

  • 输入:n = 13

  • 输出:2,因为 13 = 4 + 9

2. 理解问题和递推关系

核心思路:

对于给定的 n,我们可以依次减去某个完全平方数,剩余的部分继续递归地寻找最优解。我们需要找到所有完全平方数中的最小组合。

动态规划状态定义:

  1. dp[i]:表示数字 i 能够表示为若干个完全平方数和的最小数量。
  2. 状态转移方程
    • 对于每个 i,我们可以尝试用一个平方数 j^2 来减少 i,即:
      d p [ i ] = min ⁡ ( d p [ i ] , d p [ i − j 2 ] + 1 ) dp[i] = \min(dp[i], dp[i - j^2] + 1) dp[i]=min(dp[i],dp[ij2]+1)
    • 其中,j 是满足 j^2 <= i 的所有平方数。

状态转移思路:

  • 对于每个整数 i,我们可以枚举所有小于 i 的平方数 j^2,并通过递归减少问题的规模。例如:
    d p [ i ] = min ⁡ ( d p [ i − 1 2 ] + 1 , d p [ i − 2 2 ] + 1 , d p [ i − 3 2 ] + 1 , …   ) dp[i] = \min(dp[i - 1^2] + 1, dp[i - 2^2] + 1, dp[i - 3^2] + 1, \dots) dp[i]=min(dp[i12]+1,dp[i22]+1,dp[i32]+1,)

边界条件:

  • dp[0] = 0:数字 0 可以表示为 0 个完全平方数。

3. 解决方法

动态规划方法

  1. 初始化:创建一个数组 dp,其中 dp[i] 表示数字 i 的最少完全平方数表示数量。
  2. 状态转移:对于每个数字 i,枚举小于 i 的所有平方数,并根据转移方程更新 dp[i]
  3. 返回结果:最终 dp[n] 即为数字 n 的最少完全平方数表示数量。

伪代码:

initialize dp array with dp[0] = 0
for i from 1 to n:
    for j from 1 to sqrt(i):
        dp[i] = min(dp[i], dp[i - j^2] + 1)
return dp[n]

4. 进一步优化

  • 时间复杂度:时间复杂度为 O(n * sqrt(n)),因为对于每个 i,我们要枚举小于 i 的平方数。
  • 空间复杂度:空间复杂度为 O(n),因为我们需要存储数组 dp

5. 小总结

  • 递推思路:通过动态规划,从小到大构建 dp 数组,每个 i 的值可以通过前面的平方数和的最优解来确定。
  • 时间复杂度:时间复杂度为 O(n * sqrt(n)),空间复杂度为 O(n),适合处理中等规模的输入。

以上就是完全平方数问题的基本思路。


Python代码

class Solution:
    def numSquares(self, n: int) -> int:
        # 初始化dp数组,dp[i]表示数字i的最少完全平方数数量
        dp = [float('inf')] * (n + 1)
        dp[0] = 0  # 数字0可以表示为0个完全平方数
        
        # 动态规划计算
        for i in range(1, n + 1):
            # 枚举所有小于i的平方数
            for j in range(1, int(math.sqrt(i)) + 1):
                dp[i] = min(dp[i], dp[i - j * j] + 1)
        
        return dp[n]  # 返回数字n的最少完全平方数数量

Python代码解释

  1. 初始化:定义一个 dp 数组,dp[i] 表示数字 i 的最少完全平方数数量,初始值设为正无穷,dp[0] = 0
  2. 动态规划递推:通过枚举小于 i 的所有平方数,更新 dp[i]
  3. 返回结果:最终 dp[n] 即为最少完全平方数的数量。

C++代码

class Solution {
public:
    int numSquares(int n) {
        // 初始化dp数组,dp[i]表示数字i的最少完全平方数数量
        vector<int> dp(n + 1, INT_MAX);
        dp[0] = 0;  // 数字0可以表示为0个完全平方数
        
        // 动态规划计算
        for (int i = 1; i <= n; ++i) {
            // 枚举所有小于i的平方数
            for (int j = 1; j * j <= i; ++j) {
                dp[i] = min(dp[i], dp[i - j * j] + 1);
            }
        }
        
        return dp[n];  // 返回数字n的最少完全平方数数量
    }
};

C++代码解释

  1. 初始化:定义 dp 数组,dp[i] 表示数字 i 的最少完全平方数数量,初始值为 INT_MAXdp[0] = 0
  2. 动态规划递推:通过枚举每个数字 i,并计算小于 i 的所有平方数的组合,更新 dp[i]
  3. 返回结果:最终返回 dp[n],即最少的完全平方数数量。

总结

  • 核心思路:利用动态规划,自底向上构建 dp 数组,逐步求解每个数字 i 的最少完全平方数表示。
  • 时间复杂度:时间复杂度为 O(n * sqrt(n)),适合处理中等规模的 n
  • 空间复杂度:空间复杂度为 O(n),因为我们需要维护一个大小为 n+1 的数组。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Albert_Lsk

今天又能喝柠檬茶啦

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值