题面: 给你一个大小为 m x n 的整数矩阵 mat 和一个整数 target 。从矩阵的每一行中选择一个整数,你的目标是最小化所有选中元素之和与目标值 target 的绝对差。返回最小的绝对差 。
a 和 b 两数字的 绝对差 是 a - b 的绝对值。
来源:力扣(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
};