日常: 算法、数据结构、分布式系统、日常思考感悟等

[LeetCode] Furthest Building You Can Reach

2021.04.26

问题描述

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.

Example 1:

Input: heights = [4,2,7,6,9,14,12], bricks = 5, ladders = 1

Output: 4 Explanation: Starting at building 0, you can follow these steps:

  • Go to building 1 without using ladders nor bricks since 4 >= 2.
  • Go to building 2 using 5 bricks. You must use either bricks or ladders because 2 < 7.
  • Go to building 3 without using ladders nor bricks since 7 >= 6.
  • Go to building 4 using your only ladder. You must use either bricks or ladders because 6 < 9. It is impossible to go beyond building 4 because you do not have any more bricks or ladders.

Example 2:

Input: heights = [4,12,2,7,3,18,20,3,19], bricks = 10, ladders = 2

Output: 7

Example 3:

Input: heights = [14,3,19,3], bricks = 17, ladders = 0

Output: 3

Constraints:

  • 1 <= heights.length <= 10^5
  • 1 <= heights[i] <= 10^6
  • 0 <= bricks <= 10^9
  • 0 <= ladders <= heights.length

问题分析

分析题意可知,遇到“低–高”落差时,可以使用砖块或者梯子继续前进;且梯子没有高度限制,这是一个非常重要的隐藏条件;那么我们可以得出:在砖块有限的情况下,落差值大的应该优先使用梯子,梯子使用完后,再使用砖块;这样才能尽可能的走的远

因此,我们可以维护一个最小堆,堆元素的数量就是梯子数量;再维护一个已使用砖块的数量usebricks;然后遍历heights数组,将前ladders个较大的落差值维护在最小堆中,如果有元素从最小堆中被“排挤”出来,说明这个不是前K大的,需要使用砖块,那么usebricks加上这个值即可,最后如果usebricks如果大于砖块总数量,终止循环

综上,代码如下:

示例代码

// 最优解: 将落差最高的前ladders个使用梯子,剩下的使用bricks
// 维护一个最小堆,保存前 ladders 大的差值;
// 其余差值和记为 sum; 遍历直至 sum > bricks, 此时能够到达的最远building即为所求
func furthestBuilding(heights []int, bricks int, ladders int) int {
    ans := 0
    usebricks := 0
    minheap := NewMinheap(ladders)
    for i := 1; i < len(heights); i++ {
        gap := heights[i] - heights[i-1]
        if gap <= 0 {
            ans = i
            continue
        }
        usebricks += minheap.Add(gap)
        if usebricks > bricks {
            break
        }
        ans = i
    }

    spew.Dump(minheap)
    return ans
}

type minheap struct {
    size int
    list []int
}

func NewMinheap(size int) *minheap {
    return &minheap{
        size: size,
    }
}

func (m *minheap) Add(val int) int {
    if m.size == 0 {
        return val
    }
    // 堆未满,直接append
    if len(m.list) < m.size {
        m.list = append(m.list, val)
        // 自底向上堆化
        m.adjustFromBottom()
        return 0
    }

    // 堆已满,判断与root元素的大小关系
    if val <= m.list[0] {
        return val
    }

    // 如果比堆顶元素要大,则覆盖堆顶元素,自顶向下堆化
    res := m.list[0]
    m.list[0] = val
    m.adjustFromRoot()
    return res
}

// 自顶向下堆化
func (m *minheap) adjustFromRoot() {
    for i := 0; i < len(m.list); i++ {
        swapidx := i
        left, right := 2*i+1, 2*i+2

        // 如果不存在子树,终止循环
        if swapidx >= len(m.list) {
            break
        }

        // 如果左子树存在且左节点值小于当前值,则左节点为待交换的点
        if left < len(m.list) && m.list[i] > m.list[left] {
            swapidx = left
        }
        // 如果右子树也存在,且右节点的值小于当前值 或者 左节点,则待交换点为右节点
        if right < len(m.list) && m.list[swapidx] > m.list[right] {
            swapidx = right
        }

        // 如果待交换的点为自身,说明已经堆化完成
        if swapidx == i {
            break
        }
        m.list[i], m.list[swapidx] = m.list[swapidx], m.list[i]
        // 下一轮迭代开始时,i会自增,因此这里注意减1
        i = swapidx - 1
    }
}

// 自底向上堆化
func (m *minheap) adjustFromBottom() {
    for i := len(m.list) - 1; i >= 1; i-- {
        if m.list[i] >= m.list[(i-1)/2] {
            break
        }
        m.list[i], m.list[(i-1)/2] = m.list[(i-1)/2], m.list[i]
        i = (i-1)/2 + 1
    }
}

原题链接

Furthest Building You Can Reach

发表评论
Powered By Valine
v1.5.2