You are given an integer array heights representing the heights of buildings, some bricks, and some ladders.
You start your journey from building 0 and move to the next building by possibly using bricks or ladders.
While moving from building i to building i+1 (0-indexed),
- If the current building's height is greater than or equal to the next building's height, you do not need a ladder or bricks.
- If the current building's height is less than the next building's height, you can either use one ladder or (h[i+1] - h[i]) bricks. Return the furthest building index (0-indexed) you can reach if you use the given ladders and bricks optimally.
输入:heights = [4,2,7,6,9,14,12], bricks = 5, ladders = 1 输出:4 解释:从建筑物 0 出发,你可以按此方案完成旅程:
- 不使用砖块或梯子到达建筑物 1 ,因为 4 >= 2
- 使用 5 个砖块到达建筑物 2 。你必须使用砖块或梯子,因为 2 < 7
- 不使用砖块或梯子到达建筑物 3 ,因为 7 >= 6
- 使用唯一的梯子到达建筑物 4 。你必须使用砖块或梯子,因为 6 < 9 无法越过建筑物 4 ,因为没有更多砖块或梯子。
简单来说就是:爬楼。遇到矮的楼,直接下;遇到高的楼,可以用砖块或者梯子,梯子可以上任意高度的楼,而对于砖块,有多少块才能上多高的距离。
思考
这道题我开始想的很简单:既然梯子能上任意高度,不如一开始就把鸡肋的砖块用了,梯子留到之后再用。然后看了几个官方给的用例,似乎也没什么问题。
但是如果遇到这种情况:
输入:heights = [4,8,7,6,8,10,12], bricks = 4, ladders = 1
在这里,如果使用这个策略:砖块4 → 下 → 下 → 梯子 → 走不了了,输出4。
但正解应该是:梯子 → 下 → 下 → 砖块2 → 砖块2,→ 走不了了,输出5。
所以,正确的思路应该是:把梯子用该用的地方(落差最大的地方),其他时候尽可能用砖块。
💡 第一版代码
var furthestBuilding = function(heights, bricks, ladders) {
let maxDiff = 0; // 保存最大落差
let tempDiff = 0; // 当前的落差
let len = heights.length;
if(len === 1) {
return 1;
}
for (let i = 1; i < len; i++) {
if (heights[i] <= heights[i - 1]) {
continue;
}
tempDiff = heights[i] - heights[i - 1];
bricks -= tempDiff; // 优先使用砖块
maxDiff = Math.max(maxDiff, heights[i] - heights[i - 1]); // 记录最大的落差
if (bricks < 0) { // 当砖块不足以跨越的时候
if (ladders > 0) { // 如果有梯子,把之前最大diff的地方换成梯子
ladders--;
bricks += maxDiff; // 增加现有砖块
} else {
return i - 1; // 没梯子则止步于此
}
}
}
return len - 1;
};
看起来没啥问题(?),官方用例也通过了,但是提交之后发现报错……OTZ
问题就在于:只考虑了一次【砖换梯】情况(而且官方用例也都是这种情况,于是都能通过
正确的【砖换梯】应该是keep一个diff的优先队列,当砖块不够时,替换优先队列头部diff对应位置的砖块。
【基础知识】JS的优先队列
实现优先队列有两种方法,列表和堆。
💡 第二版代码
这道题有一点好处就是不用记录diff的位置,只要无脑把diff放入数组就行。从概率上讲出队列的几率要小一些,所以这里在出队的时候增加复杂度。
function dequeue(arr){ // 弹出一个最大数,O(n)
let temp = arr[0];
let maxIndex = 0;
for(let n = 1; n < arr.length; n++){
if(arr[n] > temp){
temp = arr[n];
maxIndex = n;
}
}
return [maxIndex, temp]
}
var furthestBuilding = function(heights, bricks, ladders) {
let maxDiff = [];
let tempDiff = 0;
let len = heights.length;
if(len === 1) {
return 1;
}
for (let i = 1; i < len; i++) {
if (heights[i] <= heights[i - 1]) {
continue;
}
tempDiff = heights[i] - heights[i - 1];
bricks -= tempDiff;
maxDiff.push(tempDiff); // 直接push,O(1)
if (bricks < 0) {
if (ladders > 0) {
ladders--;
let [index, maxValue] = dequeue(maxDiff);
bricks += maxValue;
maxDiff.splice(index, 1); //去掉【使用过的】最大数
} else {
return i - 1;
}
}
}
return len - 1;
};