给你一个整数 n
,返回 和为 n
的完全平方数的最少数量 。
完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1
、4
、9
和 16
都是完全平方数,而 3
和 11
不是。
示例 1:
输入:n = 12 输出:3 解释:12 = 4 + 4 + 4
示例 2:
输入:n = 13 输出:2 解释:13 = 4 + 9
提示:
1 <= n <= 104
我们可以使用动态规划解决这个问题
动态规划其实也就是遍历,只不过将中间值用空间储存起来了,在递归的基础上,用空间存储换时间,就是遍历了所有情形然后取的最小值,不过动态规划有自顶向底和自底向顶,前者是不需要递归的。
在动态规划实现中,我们使用的核心思路是通过之前计算的结果来得到当前状态的结果。我们需要找到和为 n
的完全平方数的最少数量,这个过程可以通过不断地组合已知的完全平方数来实现。
完全平方数的定义
完全平方数是指可以表示为一个整数的平方的数,例如 (1, 4, 9, 16, 25)。这意味着我们需要考虑这些数的组合,以找出它们的和等于 n
的方式。
动态规划的基本思路
动态规划在处理最优子结构时,通常会从小到大构建出一个解。在这个问题中,我们可以定义一个 dp
数组:
dp[i]
表示和为i
的完全平方数的最小数量。
转移方程
我们的转移方程是这样的:
- 对于每个
i
(从 1 到 n),我们检查所有可能的完全平方数j * j
,使得 (j * j < i):dp[i] = min(dp[i], dp[i - j * j] + 1)
这意味着:
- 如果你要构造总和为
i
的数,你可以使用某个完全平方数j * j
,然后再加上一个更小的数(即i - j * j
)。 - 我们通过
dp[i - j * j]
来获取构成i - j * j
的和的最小完全平方数的数量,加上 1(因为我们使用了j * j
)。
示例解析
假设我们有 n = 12
:
- 完全平方数候选数组是:[1, 4, 9]
- 计算过程:
dp[1]
= 1 (1)dp[2]
= 2 (1 + 1)dp[3]
= 3 (1 + 1 + 1)dp[4]
= 1 (4)dp[5]
= 2 (4 + 1)dp[6]
= 3 (4 + 1 + 1)dp[7]
= 4 (4 + 1 + 1 + 1)dp[8]
= 2 (4 + 4)dp[9]
= 1 (9)dp[10]
= 2 (9 + 1)dp[11]
= 3 (9 + 1 + 1)dp[12]
= 3 (4 + 4 + 4)
下面是java代码
public class PerfectSquares {
public int numSquares(int n) {
// 创建一个数组 a,a[i] 表示和为 i 的完全平方数的最少数量
int[] a = new int[n + 1];
// 初始化 a 数组
for (int i = 1; i <= n; i++) {
a[i] = Integer.MAX_VALUE; // 初始值设置为无穷大
for (int j = 1; j * j <= i; j++) {
// 更新 a[i],选择当前完全平方数
a[i] = Math.min(a[i], a[i - j * j] + 1);
}
}
return a[n]; // 返回和为 n 的最小数量
}
public static void main(String[] args) {
PerfectSquares perfectSquares = new PerfectSquares();
int n1 = 12;
System.out.println(perfectSquares.numSquares(n1)); // 输出: 3
int n2 = 13;
System.out.println(perfectSquares.numSquares(n2)); // 输出: 2
}
}
-
动态规划数组:
- 创建一个大小为
n + 1
的数组a
,其中 a[i]
表示和为i
的完全平方数的最少数量。
- 创建一个大小为
-
初始化:
- 遍历从 1 到 n,为每个 a
[i]
初始化为无穷大,表示初始状态。
- 遍历从 1 到 n,为每个 a
-
动态规划转移:
- 对于每个
i
,再对每个完全平方数j * j
进行遍历(其中j
从 1 开始,直到j * j
大于i
)。 - 更新 a
[i]
的值,选择当前的完全平方数j * j
,并通过 a[i - j * j] + 1
更新最小数量。
- 对于每个
-
返回结果:
- 最后,返回 a
[n]
,即和为n
的完全平方数的最少数量。
- 最后,返回 a
- 时间复杂度: O(n * √n),因为每个
i
都要遍历所有小于等于√i
的完全平方数。 - 空间复杂度: O(n),用于存储动态规划数组
a
。