leetcode1981. 最小化目标值与所选元素的差

689 阅读2分钟

题面: 给你一个大小为 m x n 的整数矩阵 mat 和一个整数 target 。从矩阵的每一行中选择一个整数,你的目标是最小化所有选中元素之和与目标值 target 的绝对差。返回最小的绝对差 。

a 和 b 两数字的 绝对差 是 a - b 的绝对值。

image.png

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/mi… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

这是一个背包问题. 我们可以设数组 f[i][j] 表示矩阵的前 i 行, 和为 j 的状态存在与否

例如:

  • f[1][1] == true 就表示该矩阵的前 1 行存在和为 1 的状态(mat[0][0])
  • f[2][5] == true 就表示该矩阵的前 2 行存在和为 5 的状态(mat[0][0] + mat[1][0])
  • ...

由于题目要求的答案是: 每行取一个元素, 最后相加得到的结果与 target 的绝对差, 且要求绝对差最小

状态转移方程: f[i][j] = f[i][j] || f[i - 1][j - w[k]]

最初代码

var minimizeTheDifference = function(mat, target) {
  const m = mat.length
  const n = mat[0].length
  const max = 4900 // 最多有 70 行, 每个元素最大值为 70
  const f = new Array(m + 1).fill(0).map(item => new Array(max + 1).fill(false))

  f[0][0] = true

  for (let i = 1; i <= m; i ++) {
    let w = mat[i - 1]
    for (let k = 0; k < n; k ++) {
      for (let j = w[k]; j <= max; j ++) {
        f[i][j] = f[i][j] || f[i - 1][j - w[k]] 
        // 如果 f[i][j] 已经为 true 了, 则不能再改为 false
      }
    }
  }

  let min = 5000
  for (let i = 1, abs; i <= max; i ++) {
    if (f[m][i]) {
      abs = Math.abs(target - i)
      if (abs < min) min = abs
      else return min
    }
  }
  return min
};

改进代码

可以简单的发现, 在第三个 for 循环的 j 经过了太多的无用遍历, 同时 f 只需要第 i 项和第 i - 1 项即可, 所以可以用滚动数组优化空间复杂度

var minimizeTheDifference = function(mat, target) {
  const m = mat.length
  const n = mat[0].length
  const max = 4900
  const f = new Array(2).fill(0).map(item => new Array(max + 1).fill(false))

  f[0][0] = true

  for (let i = 1, r = 0; i <= m; i ++) {
    let w = mat[i - 1]
    let cur = i % 2, pre = (i - 1) % 2
    f[cur].fill(false)
    for (let k = 0, newR = r; k <= newR; k ++) {
      if (f[pre][k]) { // 根据前一行的状态推出该行的状态
        for (let j = 0; j < n; j ++) {
          f[cur][k + w[j]] = true
          r = Math.max(r, k + w[j])
        }
      }
    }
  }

  let min = 5000
  for (let i = 1, abs; i <= max; i ++) {
    if (f[m % 2][i]) {
      abs = Math.abs(target - i)
      if (abs < min) min = abs
      else return min
    }
  }
  return min
};