力扣第279题 完全平方数 c++ 附java代码 (完全背包)动态规划问题

题目

279. 完全平方数

中等

相关标签

广度优先搜索 数学 动态规划

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

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

示例 1:

复制代码
输入:n = 12

输出:3 
解释:12 = 4 + 4 + 4

示例 2:

复制代码
输入:n = 13

输出:2
解释:13 = 4 + 9

提示:

  • 1 <= n <= 104

思路和解题方法

  • 在两个版本中,都使用了一个一维数组 dp 来保存状态。其中,dp[i] 表示当目标数为 i 时,需要的最小完全平方数和。初始值为 INT_MAX 表示不可达。
  • 在第一个版本中,我们通过遍历背包和物品来更新状态。外层循环遍历背包,内层循环遍历物品,对于每一个背包 i,我们尝试将某个完全平方数 j*j 放入背包,然后更新状态。递推公式为:dp[i] = min(dp[i - j * j] + 1, dp[i]),表示将 j*j 放入背包后,需要的完全平方数和为 dp[i - j * j] + 1,与之前的状态 dp[i] 比较取较小值。
  • 而在第二个版本中,我们通过遍历物品和背包来更新状态。外层循环遍历物品,内层循环遍历背包,对于每一个物品 i*i,我们尝试将其放入背包 j 中,然后更新状态。递推公式为:dp[j] = min(dp[j - i * i] + 1, dp[j]),表示将 i*i 放入背包 j 后,需要的完全平方数和为 dp[j - i * i] + 1,与之前的状态 dp[j] 比较取较小值。
  • 两个版本的代码实现思路是一样的,只是遍历的顺序不同,但最终得到的结果是相同的。

复杂度

时间复杂度:

O(n * sqrt(n)) (sqrt()为开方函数)

时间复杂度:

两个版本的代码都使用了嵌套循环。最外层的循环遍历的次数都是目标数 n,内层的循环遍历的次数都是完全平方数的个数,而最大的完全平方数为 sqrt(n),因此内层循环的时间复杂度为 O(sqrt(n))。因此,两个版本的代码的时间复杂度都是 O(n * sqrt(n))。

空间复杂度

O(n)

空间复杂度:

两个版本的代码都使用了一个一维数组 dp 来记录状态。数组的长度为 n + 1,因此空间复杂度为 O(n)。而其他变量的空间复杂度都是常数级别的。

c++ 代码

cpp 复制代码
// 版本一
class Solution {
public:
    int numSquares(int n) {
        vector<int> dp(n + 1, INT_MAX); // 创建一个大小为 n+1 的数组,初始化为 INT_MAX,表示不可达状态
        dp[0] = 0; // 初始条件,当目标数为 0 时,需要的最小完全平方数和为 0
        for (int i = 0; i <= n; i++) { // 遍历背包,i 表示当前目标数
            for (int j = 1; j * j <= i; j++) { // 遍历物品,j*j 表示完全平方数
                dp[i] = min(dp[i - j * j] + 1, dp[i]); // 更新状态,取较小值
            }
        }
        return dp[n]; // 返回目标数为 n 时的最小完全平方数和
    }
};

// 版本二
class Solution {
public:
    int numSquares(int n) {
        vector<int> dp(n + 1, INT_MAX); // 创建一个大小为 n+1 的数组,初始化为 INT_MAX,表示不可达状态
        dp[0] = 0; // 初始条件,当目标数为 0 时,需要的最小完全平方数和为 0
        for (int i = 1; i * i <= n; i++) { // 遍历物品,i 表示当前物品,即完全平方数
            for (int j = i * i; j <= n; j++) { // 遍历背包,j 表示当前目标数
                dp[j] = min(dp[j - i * i] + 1, dp[j]); // 更新状态,取较小值
            }
        }
        return dp[n]; // 返回目标数为 n 时的最小完全平方数和
    }
};

Java代码

java 复制代码
class Solution {
    // 版本一,先遍历物品, 再遍历背包
    public int numSquares(int n) {
        int max = Integer.MAX_VALUE;
        int[] dp = new int[n + 1];
        // 初始化
        for (int j = 0; j <= n; j++) {
            dp[j] = max;
        }
        // 当和为0时,组合的个数为0
        dp[0] = 0;
        // 遍历物品
        for (int i = 1; i * i <= n; i++) {
            // 遍历背包
            for (int j = i * i; j <= n; j++) {
                // 更新状态,取较小值
                dp[j] = Math.min(dp[j], dp[j - i * i] + 1);
		// 不需要这个if判断语句,因为在完全平方数这一问题中不会有"凑不成"的情况发生(一定可以用"1"来组成任何一个n),所以注释掉了这个if语句。
            }
        }
        return dp[n]; // 返回目标数为 n 时的最小完全平方数和
    }
}

class Solution {
    // 版本二, 先遍历背包, 再遍历物品
    public int numSquares(int n) {
        int max = Integer.MAX_VALUE;
        int[] dp = new int[n + 1];
        // 初始化
        for (int j = 0; j <= n; j++) {
            dp[j] = max;
        }
        // 当和为0时,组合的个数为0
        dp[0] = 0;
        // 遍历背包
        for (int j = 1; j <= n; j++) {
            // 遍历物品
            for (int i = 1; i * i <= j; i++) {
                // 更新状态,取较小值
                dp[j] = Math.min(dp[j], dp[j - i * i] + 1);
            }
        }
        return dp[n]; // 返回目标数为 n 时的最小完全平方数和
    }
}

觉得有用的话可以点点赞,支持一下。

如果愿意的话关注一下。会对你有更多的帮助。

每天都会不定时更新哦 >人< 。

相关推荐
paopaokaka_luck5 分钟前
基于Spring Boot+Vue的助农销售平台(协同过滤算法、限流算法、支付宝沙盒支付、实时聊天、图形化分析)
java·spring boot·小程序·毕业设计·mybatis·1024程序员节
Leo.yuan6 分钟前
39页PDF | 华为数据架构建设交流材料(限免下载)
数据结构·华为
m0_594526309 分钟前
Python批量合并多个PDF
java·python·pdf
咕哧普拉啦12 分钟前
乐尚代驾十订单支付seata、rabbitmq异步消息、redisson延迟队列
java·spring boot·mysql·spring·maven·乐尚代驾·java最新项目
✿゚卡笨卡13 分钟前
pdf 添加页眉页脚,获取前五页
java·pdf
小乖兽技术17 分钟前
C#与C++交互开发系列(二十):跨进程通信之共享内存(Shared Memory)
c++·c#·交互·ipc
半夜不咋不困20 分钟前
单链表OJ题(3):合并两个有序链表、链表分割、链表的回文结构
数据结构·链表
王俊山IT22 分钟前
C++学习笔记----10、模块、头文件及各种主题(二)---- 预处理指令
开发语言·c++·笔记·学习
斌斌_____38 分钟前
通过反射机制,比较两个对象的字段值的差异
java
幼儿园园霸柒柒41 分钟前
第七章: 7.3求一个3*3的整型矩阵对角线元素之和
c语言·c++·算法·矩阵·c#·1024程序员节