2020-04-14 (DP)
春天到了,走在北京街头,满大街的杨絮迎面扑来,仿佛生存在培养液中。
猎头和 HR 活动的也更频繁了,看来复工的公司多起来了。
338. Counting Bits
在纸上写一下推到公式,从 0 写到 8 就可以发现规律。2,4,8 的 ‘1’是一致的,6,3是一致的。所以可以得到推导公式 dp[n] = dp[n >> 1] + (n & 1)。n & 1是判断最右边是否有 1, 防止 bit right shift 时最右侧的 1 算掉了。
/**
* @param {number} num
* @return {number[]}
*/
var countBits = function (num) {
const arr = new Array(num + 1);
arr[0] = 0;
for (let i = 1; i <= num; i++) {
arr[i] = arr[i >> 1] + (i & 1);
}
return arr;
};
304. Range Sum Query 2D - Immutable
本来打算做 1314 那道题。题目看不明白,有人说是 304 的变种,那就先做 304。明天再做 1314。
关于 2 维数组的 dp 生成,可以总结一个固定模板,第一行,第一列和剩下的,总共三部分。代码很类似。
这个卡了很久的地方是 sumRegion 的 corner case,当左上端点(row1,col1) 就在边界时,要判断一下,如果在边界上则不用多减了。
/**
* @param {number[][]} matrix
*/
var NumMatrix = function (matrix) {
const r = matrix.length;
if (r === 0) return 0;
const c = matrix[0].length;
const dp = matrix.slice();
for (let i = 0; i < r; i++) {
for (let j = 0; j < c; j++) {
if (i === 0 && j !== 0) {
// 加和第一行
dp[i][j] = dp[i][j - 1] + matrix[i][j];
} else if (i !== 0 && j === 0) {
// 加和第一列
dp[i][j] = dp[i - 1][j] + matrix[i][j];
} else if (i > 0 && j > 0) {
// 加和其他所有
dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1] + matrix[i][j];
}
}
}
this.dp = dp;
};
/**
* @param {number} row1
* @param {number} col1
* @param {number} row2
* @param {number} col2
* @return {number}
*/
NumMatrix.prototype.sumRegion = function (row1, col1, row2, col2) {
const { dp } = this;
const total = dp[row2][col2];
const left = col1 - 1 >= 0 ? dp[row2][col1 - 1] : 0;
const top = row1 - 1 >= 0 ? dp[row1 - 1][col2] : 0;
const overlap = row1 - 1 >= 0 && col1 - 1 >= 0 ? dp[row1 - 1][col1 - 1] : 0;
return total - left - top + overlap;
};
/**
* Your NumMatrix object will be instantiated and called as such:
* var obj = new NumMatrix(matrix)
* var param_1 = obj.sumRegion(row1,col1,row2,col2)
*/
746. Min Cost Climbing Stairs
刷这题之前,先回顾一下爬楼梯(70 题)。生成一个简单的 Fibonacci 数列(写了一遍,还是第一直觉用的递归,不出所料,超时了。然后看了两年前的提交,也是一开始用的递归,同样的超时。老复读机了。)
这道题在爬楼梯的基础上增加了一步,原来我们需要记录的在每一个台阶时爬楼梯的方法,现在我们需要记录的是爬到当前台阶时的最小开销。有了这个思想,问题迎刃而解。最后 return 的时候,返回倒数第一或倒数第二级台阶的最小开销。
我习惯不去污染 input,所以创建一个新的数组记录 dp 的数值。其实是可以 inplace 修改 cost,这样空间更小。
/**
* @param {number[]} cost
* @return {number}
*/
var minCostClimbingStairs = function (cost) {
if (cost.length <= 2) return Math.min.apply(null, cost);
const len = cost.length;
const dp = cost.slice();
let i = 2;
while (i < len) {
dp[i] = Math.min(dp[i - 1], dp[i - 2]) + cost[i];
i++;
}
return Math.min(dp[len - 1], dp[len - 2]);
};