[周赛:完全背包] 2310. 个位数字为 K 的整数之和

173 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第30天,点击查看活动详情

每日刷题 2022.06.28

题目

  • 给你两个整数 num 和 k ,考虑具有以下属性的正整数多重集:
    • 每个整数个位数字都是 k 。
    • 所有整数之和是 num 。
  • 返回该多重集的最小大小,如果不存在这样的多重集,返回 -1 。
  • 注意:
    • 多重集与集合类似,但多重集可以包含多个同一整数,空多重集的和为 0 。
    • 个位数字是数字最右边的数位。

示例

  • 示例1
输入:num = 58, k = 9
输出:2
解释:
多重集 [9,49] 满足题目条件,和为 58 且每个整数的个位数字是 9 。
另一个满足条件的多重集是 [19,39] 。
可以证明 2 是满足题目条件的多重集的最小长度。
  • 示例2
输入: num = 37, k = 2
输出: -1
解释: 个位数字为 2 的整数无法相加得到 37 。
  • 示例3
输入: num = 0, k = 7
输出: 0
解释: 空多重集的和为 0 。

提示

  • 0 <= num <= 3000
  • 0 <= k <= 9

解题思路

  • 分析题目:每个整数的个位数都要是k,需要返回多重集的最小大小,也就是需要返回最小的整数个数在这个多重集中。并且每个整数是可以使用多次的。
  • 看到这里:就想到了背包问题🎒,给你一个背包的大小(多重集大小),再给你几个物品的重量(每个整数),那么需要你返回能够将这个背包装满的最小物品个数(多重集中整数个数的最小)。又因为一个物品可以使用多次(每个整数可以使用多次),那么就可以使用完全背包来解决问题

解题步骤

  • 首先,需要预处理一下符合要求的整数,也就是个位数为k的数,并且这些整数的和为num,因此只需要从1 ~ num中提取出符合要求的整数就可以,存储到ans数组中。
  • 完全背包的状态转移方程式:f[i][j] = Math.min(f[i - 1][j], f[i - 1][j - z * ans[i - 1]] + z),表示:对于前i个整数而言,和为j = num的最小多重集中整数的个数。
    • f[i - 1][j],表示:对于前i - 1个整数而言,和为j = num的最小多重集中整数的个数。
    • f[i - 1][j - z * ans[i - 1]] + z 表示:对于前i - 1个整数而言,和为j - z * ans[i - 1]的最小的多重集中整数的个数,还需要加上z,也就是当前的i整数个数。
    • 取两者之间的最小值。
  • 后面就直接使用完全背包的板子就可以了,需要注意⚠️:不论个位数为几,都不能将0存放在预处理的数组中,因为0会导致在第三层循环中,z * ans[i - 1] <= j 会一直小于j,那么就会死循环。 从另一个方面来说,我们要求的是最小的多重集大小,0不能够给最终的和num做贡献,却增长了多重集的长度,因此0一定是不需要的。
  • 进阶:可以将二维dp简化为一维的,实现空间上的优化。

AC代码

/**
 * @param {number} num
 * @param {number} k
 * @return {number}
 */
var minimumNumbers = function(num, k) {
  if(num == 0) return 0;
  let ans = [];
  for(let i = 1; i <= num; i++) {
    if(i % 10 == k) {
      ans.push(i);
    }
  }
  // console.log(ans)
  let n = ans.length, f = new Array(n + 1).fill(0).map(() => new Array(num + 1).fill(0));
  for(let i = 1; i <= num; i++) {
    f[0][i] = Infinity;
  }
  for(let i = 1; i <= n; i++) {
    for(let j = 0; j <= num; j++) {
      f[i][j] = f[i - 1][j];
      // console.log(f[i][j], i,j)
      for(let z = 0; z * ans[i - 1] <= j; z++) {
         // console.log('zz::::', z, ans[i - 1])
        if(f[i - 1][j - z * ans[i - 1]] != Infinity) {
          f[i][j] = Math.min(f[i][j], f[i - 1][j - z * ans[i - 1]] + z);
        }  
      }
    }
  }
  return f[n][num] == Infinity ? -1 : f[n][num];
};