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

Posted GoldenaArcher

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[JavaScript 刷题] DP - 组成整数的最小平方数数量, leetcode 279相关的知识,希望对你有一定的参考价值。

[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) 的原因。

具体实现方式如下:

  1. 新建一个长度为 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 等。

  2. 从 index 1 开始计算

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

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

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

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

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

  4. index = 3

    3 的获取方式同理:

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

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

  5. index = 4

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

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

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

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

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

  6. index = 5

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

  7. 重复步骤。。。

遍历完整个数组的长度之后,整个数组都会被填充完成,因此直接返回 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));
开发者涨薪指南 48位大咖的思考法则、工作方式、逻辑体系

以上是关于[JavaScript 刷题] DP - 组成整数的最小平方数数量, leetcode 279的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode刷题--点滴记录019

刷题deque优化dp理想的正方形

刷题笔记(17)--整数拆分/剪绳子(动态规划)

[JavaScript 刷题] DP - 买卖股票的最佳时机,leetcode 121

[JavaScript 刷题] DP - 杨辉三角, leetcode 118

[JavaScript 刷题] DP - 最大子数组和, leetcode 79