Skip to content

完全平方数

题目描述

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

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

示例 1:

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

题目链接:https://leetcode.cn/problems/perfect-squares

文章讲解:https://programmercarl.com/0279.完全平方数.html

思路

依然使用完全平方数凑成n。背包容量为整数n,物品为完全平方数。

和为 n 的完全平方数的最少数量 就是问物品所需最少数量。从示例上看物品可以重复使用。

所以这是一个完全背包问题,和零钱兑换一模一样

动规五部曲

1、dp数组及下标含义

dp[j]:凑足整数 j 所需完全平方数的最少数量为dp[j]

2、递推公式

在【0,i*i 】完全平方数 中考虑,凑足和为 j 时 完全平方数数量最少 有两种情况:

情况1、 j 小于 i*i ,这时 i*i 根本放不进背包,溢出了。意味着[0,i*i] 时dp[j] == [0,(i-1)*(i-1)]时dp[j]

情况2、 j 大于等于 i*i,这时考虑 i*i 在不在其中。

- 情况2.1 在,那么`dp[j] = dp[j-i*i]+1`。
- 情况2.2 不在,那么`dp[j] = dp[j]`,和第一种情况相同
- 情况2.1和2.2之间选个小的
C++
dp[j] = min(dp[j],dp[j-i*i]+1);
3、dp数组初始化

因为整数n从1开始,因此 dp[0] 没有意义,那么我们初始化成什么?。很明显dp[1]=1,那么为了能够满足递推公式求出dp[1],应该dp[0]=0, 而dp[1] 初始化为比1大的数。但是为了保持一致,顺利求出其他非0下标的dp[j],因此其余非0下标的dp[j]为INT_MAX

4、确定遍历顺序

外层for遍历背包,内层for遍历物品,还是外层for遍历物品,内层for遍历背包,都是可以的!

5、举例推导

n为5例,dp状态图如下:

遍历背包再遍历物品的示例,未显示完全,

279.完全平方数

代码实现

C++
int numSquares(int n) {
    std::vector<int> dp(n + 1, INT_MAX);
    dp[0] = 0;
    for(int i = 1;i*i <= n;i++){
        for(int j = i*i;j <= n;j++){
            dp[j] = std::min(dp[j],dp[j - i*i] + 1); 
        }
    }
    return dp[n];
}