给你一个整数 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
1. 生活案例:装修铺地砖
想象你正在装修一个面积为 的长条形房间。装修市场比较奇怪,他们只卖“正方形地砖”,且边长必须是整数。
-
可选的地砖:面积分别为 ,,,(这些就是完全平方数)。
-
你的任务:用最少数量的地砖,刚好把面积为 的地面铺满。
-
策略:
-
为了铺满面积为 的地面:
- 方案 A:全用 的,需要 块。
- 方案 B:用 的(面积为 4),,需要 3 块。
- 对比发现方案 B 更省砖!
-
2. 代码解析与“生活化”注释
这段代码通过不断尝试“最后一块砖用多大”,来反推最少数量。
JavaScript
/**
* @param {number} n - 房间的总面积
* @return {number} - 最少需要的地砖数量
*/
var numSquares = function(n) {
// dp[i] 代表:铺满面积为 i 的房间,最少需要多少块地砖
// 初始值给 n(全铺 1x1 地砖的情况),这已经是最大可能的数量了
let dp = new Array(n + 1).fill(n);
// 基础情况:面积为 0 时,需要 0 块地砖
dp[0] = 0;
// 面积为 1 时,只能用 1x1 的,需要 1 块
dp[1] = 1;
// i 代表当前我们要计算的“目标面积”,从 1 逐渐算到 n
for (let i = 1; i <= n; i++) {
// j 代表地砖的边长,j*j 就是地砖的面积(1, 4, 9, 16...)
// 我们尝试所有“面积不大于剩余空间”的正方形砖
for (let j = 1; j * j <= i; j++) {
// 生活化解释:
// 我尝试拿出一块面积为 j*j 的正方形砖
// 那么总砖数 = 铺满剩下面积 (i - j*j) 的最少砖数 + 1 (当前这一块)
// 我们在各种可能的 j 中,选一个能让 dp[i] 最小的
dp[i] = Math.min(dp[i], dp[i - j * j] + 1);
}
}
// 返回铺满面积 n 的最少砖数
return dp[n];
};
3. 为什么代码这样写?(算法关联)
-
它就是零钱兑换:
-
在“零钱兑换”里,硬币是
coins = [1, 2, 5]。 -
在这里,硬币是
coins = [1, 4, 9, 16, ...]。 -
逻辑完全一致:
-
-
内层循环的边界:
j * j <= i。这很聪明,因为你不可能用一块面积比房间还大的地砖。 -
计算顺序:先算小房间(面积 1, 2, 3...),因为算大房间(面积 12)时,需要用到小房间(比如面积 8)的结果。