279.完全平方数(dfs)

352 阅读2分钟

「这是我参与2022首次更文挑战的第21天,活动详情查看:2022首次更文挑战」。

每日刷题第43天 2021.02.10

279.完全平方数

题目

  • 给你一个整数 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

解法

  • 思路的转换:之前书写的dfs大部分都是使用全局变量的方式,在后期的做题过程中,发现全局变量对于剪枝等操作非常的不方便,因此学习了其他的dfs写法,主要是将当前获取的值作为函数的返回值返回。

有返回值的dfs

  • dfs边界返回的条件:当所有的平方数均减完,最终结果为0,则返回0return 0

纯纯的模拟dfs

  • 首先,预处理:计算出比当前数n小的所有完全平方数,并将其存储在数组template中。
  • 封装一个nearBy函数,用于找出距离当前数最近的完全平方数.(便于后期在dfs中直接调用使用)
  • 核心dfs函数:返回值需要返回当前n的完全平方数的最少数量。
    • 返回条件(dfs一定要写一个返回的条件,否则将会一直递归下去,那么就一直往深处走,不会再往回走)
    • 接下来,查找当前n的最近的完全平方数,因为每次dfs查找下去的数,需要减去的完全平方数一定比当前的值小。
    • for循环,将当前n所有能够由完全平方数组成的方式全部遍历到,并将其所有的数量,取min最小值。
      • 举例:n = 12,比是12小的完全平方数有:9、4、1
      • 第一种情况:12 -> 9 -> 1 -> 1 -> 1
      • 第二种情况:12 -> 4 -> 4 -> 4
        • 4存在两种情况: 4 或者 1 -> 1 -> 1 -> 1
      • 第三种情况:12 -> 1 -> 1 -> 1 -> 1 -> 1 -> 1 -> 1 -> 1 -> 1 -> 1 -> 1
      • 将三种情况获得的长度取min,就是和为当前的n的完全平方数的最少数量。
    • 切记: 一定要返回每一层已经得到的最少数量。否则就会导致只有递归到返回条件才有数值,其他的均为NaN,本质上就是没有返回值导致的错误。return num

剪枝操作

  • 在每次返回当前层的最少数量之前,将数据存储下来,避免下次再递归到这里重复计算。
  • 在每次进入dfs函数的时候,添加判断条件当前的n是否已经维护过最少数量,如果存在,直接返回使用即可。
/**
 * @param {number} n
 * @return {number}
 */
var numSquares = function(n) {
  // 打表,先将当前数小于的完全平方数存储起来
  // 每次查找到当前数距离最近的完全平方数
  // 找到即:返回长度
  let template = [];
  for(let i = 1;;i++) {
    if(i * i > n) break;
    if(i * i == n) return 1;
    template[i] = i * i;
  }
  // console.log(template);
  let nearBy = function near(m) {
    for(let i = template.length - 1; i >= 1; i--) {
      if(m >= template[i]) return i;
    }
  }
  // console.log(template,nearBy(126));

  // let num = 0;
  let min = Infinity;
  // let ans = 0;
  let visited = new Map();
  function dfs(nearN) {
    if(visited.has(nearN)){
      return visited.get(nearN);
    }
    // console.log('nearN: ',nearN);
    if(nearN == 0){
      //ans = num;
      // if(num <= min){
        // min = num;
      // }
      return 0;
    }
    let tempt = nearBy(nearN);
     // 需要for循环执行BFS
    let num = Infinity;
    for(let i = tempt; i >= 1; i--) {
      // num++;
      // 当前递归出来的值
      num = Math.min(dfs(nearN - template[i]) + 1,num);
      // visited.set(nearN,Math.min(num,visited.get(nearN)));
      // num--;
    }
    visited.set(nearN,num);
    // console.log('num: ',num);
    return num;
  }
  return dfs(n);
  // return visited.get(n);
};