[JavaScript 刷题] DP - 组成整数的最小平方数数量, leetcode 279

[JavaScript 刷题] DP - 组成整数的最小平方数数量, leetcode 279,第1张

[JavaScript 刷题] DP - 组成整数的最小平方数数量, leetcode 279

上一篇 组成整数的最小平方数数量, leetcode 279 中使用了 BFS 的方式实现,这里使用 DP 完成。

其实这道题和 DP 地经典切段、选择硬币的套路还是一样的。

279. Perfect Squares

题目如下:

Given an integer n, return the least number of perfect square numbers that sum to n.

A perfect square is an integer that is the square of an integer; in other words, it is the product of some integer with itself. For example, 1, 4, 9, and 16 are perfect squares while 3 and 11 are not.

解题思路

和常用的 DP 解决方案一样,这里也会需要一个容器去保存之前计算过的数值,毕竟递归的时间复杂度是 2 n 2^n 2n,将之前计算过的数值保存在容器中这一过程又称之为 memoization,也是 React 里面的 useMemo 的技巧。

这里有一个遍历是逃不掉的,就是完美平方数的遍历,以之前画的图为例:

每一层都需要对 1, 4, 9 进行迭代。

使用 memoization 的技巧就在于,当进行 degree = 3 的迭代是,degree = 2 的值会被保存在容器中,因此无需使用递归再一次进行计算。这也就是斐波那契数列在使用递归方法的复杂度为 O ( 2 n ) O(2^n) O(2n),但是使用 DP 的复杂度却只有 O ( n ) O(n) O(n) 的原因。

具体实现方式如下:

新建一个长度为 n+1 的数组

这里直接初始化成 n+1 是因为数组是 0-based indexed,而且后面会需要用到 0 + k 2 0 + k^2 0+k2 获取完美平方数本身。

后面做数值相加的时候,如 13 的组合包含 9 + 4 9 + 4 9+4,也就是 3 2 + 2 2 3^2 + 2^2 32+22,这样可以省去一步重复计算的步骤。

这里初始化的值可以用 n,也可以取无穷大,差别不是很大。

因为已知返回最大值是 n 自身——也就是 n 1 2 1^2 12 相加,这些可以从一些边界值可以得出,如 12, 3 等。

从 index 1 开始计算

这里获取 1 的唯一方式是 0 + 1 2 0 + 1^2 0+12,在对比 n n n 0 + 1 2 0 + 1^2 0+12 后取较小值,也就是 1.

index = 2
这里开始会进入使用 memoization 的情况。

2 的获取方式其实是这么来的:

a r r [ 1 ] + 1 2 arr[1] + 1^2 arr[1]+12

分别来源于 1 2 1^2 12 ——从完美平方数的循环中获取,加上获取 1 这个值所需的最小值。

index = 3

3 的获取方式同理:

a r r [ 2 ] + 1 2 arr[2] + 1^2 arr[2]+12

i < n ii<n 的值都已经被记录在了容器中,因此这里可以直接调取,并且加上完美平方数的值,获取最小值。

index = 4

从 4 之后开始就已经包含一些特殊值了,因为 4 的获取方式可以通过 2 2 2^2 22,这是一个完美平方数的值。

从 4 以后的数值会遇到两种情况:

通过完美平方数本身可以获取通过组合完美平方数进行获取

在这种情况下就要进行判断,并且取最小值,这里的对比就是:

n n n 0 + 2 2 0 + 2^2 0+22 的对比,取最小值自然是 1 了。

index = 5

这里也是同样的方式进行 *** 作,最后判断 a r r [ 2 ] + 1 2 arr[2] + 1^2 arr[2]+12 为最优解。

重复步骤。。。

遍历完整个数组的长度之后,整个数组都会被填充完成,因此直接返回 arr[n] 即可。

使用 JavaScript 解题

DP 的代码会比 BFS 的简单一些,需要注意的就是外层的 for 循环为 [1, 2, ..., n],第二层循环体的条件为 [1, ..., i]

第二层循环为了获取完美平方数,因此当完美平方数大于当前值的时候,就可以跳出这一层循环了。

nums[i] = Math.min(nums[i], 1 + nums[i - squareVal]); 是为了更新当前所能获取的最小值。

/**
 * @param {number} n
 * @return {number}
 */
var numSquares = function (n) {
  const nums = new Array(n + 1).fill(n);
  nums[0] = 0;

  for (let i = 1; i <= n; i++) {
    for (let j = 1; j < i + 1; j++) {
      const squareVal = j * j;
      if (squareVal > i) break;

      nums[i] = Math.min(nums[i], 1 + nums[i - squareVal]);
      squareVal;
    }
  }

  return nums[n];
};

console.log(numSquares(3));
console.log(numSquares(4));
console.log(numSquares(12));
console.log(numSquares(13));

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/web/1322375.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-06-12
下一篇2022-06-12

发表评论

登录后才能评论

评论列表(0条)

    保存