2532. 过桥的时间

121 阅读3分钟

题目:
共有 k 位工人计划将 n 个箱子从旧仓库移动到新仓库。给你两个整数 nk,以及一个二维整数数组 time ,数组的大小为 k x 4 ,其中 time[i] = [leftToRighti, pickOldi, rightToLefti, putNewi]

一条河将两座仓库分隔,只能通过一座桥通行。旧仓库位于河的右岸,新仓库在河的左岸。开始时,所有 k 位工人都在桥的左侧等待。为了移动这些箱子,第 i 位工人(下标从 0 开始)可以:

  • 从左岸(新仓库)跨过桥到右岸(旧仓库),用时 leftToRighti 分钟。
  • 从旧仓库选择一个箱子,并返回到桥边,用时 pickOldi 分钟。不同工人可以同时搬起所选的箱子。
  • 从右岸(旧仓库)跨过桥到左岸(新仓库),用时 rightToLefti 分钟。
  • 将箱子放入新仓库,并返回到桥边,用时 putNewi 分钟。不同工人可以同时放下所选的箱子。

如果满足下面任一条件,则认为工人 i效率低于 工人 j

  • leftToRighti + rightToLefti > leftToRightj + rightToLeftj
  • leftToRighti + rightToLefti == leftToRightj + rightToLeftji > j

工人通过桥时需要遵循以下规则:

  • 如果工人 x 到达桥边时,工人 y 正在过桥,那么工人 x 需要在桥边等待。
  • 如果没有正在过桥的工人,那么在桥右边等待的工人可以先过桥。如果同时有多个工人在右边等待,那么 效率最低 的工人会先过桥。
  • 如果没有正在过桥的工人,且桥右边也没有在等待的工人,同时旧仓库还剩下至少一个箱子需要搬运,此时在桥左边的工人可以过桥。如果同时有多个工人在左边等待,那么 效率最低 的工人会先过桥。

所有 n 个盒子都需要放入新仓库,请你返回最后一个搬运箱子的工人 到达河左岸 的时间。

算法:
方法一:模拟
河的左右两边各有一个仓库和过桥队列,对仓库来说,我们要往仓库新增,删除,和快速找到时间最小的工人,对于过桥队列来说我们要往仓库新增,删除,和快速找到效率最低的工人,容易想到使用堆完成这些操作。

1.问题在于时间怎么表示,堆中应该存储什么数据?我们设计一个在堆中存储数据[workerID, time](time表示工人离开仓库的时间),并对times按照效率进行稳定排序,workerID越大效率越低,可以使用堆维护时间最小和效率最低的工人。

仓库队列按照时间维护最小堆,过桥队列按照效率维护最小堆(效率最小优先取出) 2.左侧过桥队列出工人的时候,n--,因为右侧旧仓库没有箱子了就不要过桥了,否则会阻塞右侧工人过桥。
3.当前时间初始化cur=0,将左右两侧的仓库队列中,时间小于等于cur的加入过桥队列。然后先对右侧队列过桥,再对左侧队列过桥。
4.n==0时终止循环,将右侧的仓库队列和等待队列清空,同时更新当前时间cur,最后的cur就是答案。

import "container/heap"
import "sort"
func findCrossingTime(n int, k int, time [][]int) int {
    // time排序,按照效率越低越后面
    sort.SliceStable(time, func(i, j int) bool {
        return time[i][0] + time[i][2] < time[j][0] + time[j][2]
    })
    // 初始化4个堆
    workL, workR := heapByTime{}, heapByTime{}
    waitL, waitR := heapByID{}, heapByID{}
    for i := k - 1; i >= 0; i -- {
        heap.Push(&waitL, pair{i, time[i][0]})
    }
    cur := 0
    // 循环直到n==0
    for n > 0 {
        // 仓库队列出队列,进入等待队列
        for len(workL) != 0 && workL[0].t <= cur {
            heap.Push(&waitL, heap.Pop(&workL))
        }
        for len(workR) != 0 && workR[0].t <= cur {
            heap.Push(&waitR, heap.Pop(&workR))
        }
        // 过桥队列出队列,更新cur时间
        // 右侧过桥队列先过桥
        if len(waitR) != 0 {
            p := heap.Pop(&waitR).(pair)
            cur = cur + time[p.i][2]
            p.t = cur + time[p.i][3]
            heap.Push(&workL, p)
        } else if len(waitL) != 0  {
            p := heap.Pop(&waitL).(pair)
            cur = cur + time[p.i][0]
            p.t = cur + time[p.i][1]
            heap.Push(&workR, p)
            n --
        } else if len(workL) == 0 {
            cur = workR[0].t
        } else if len(workR) == 0 {
            cur = workL[0].t
        } else {
            cur = min(workR[0].t, workL[0].t)
        }
    }


    // 清空右侧仓库队列和右侧等待队列,更新cur时间
    for len(workR) != 0 {
        p := heap.Pop(&workR).(pair)
        cur = max(cur, p.t) + time[p.i][2]
    }
    return cur
}


type pair struct{i int; t int}

type heapByID []pair
func (h heapByID) Less(i, j int) bool {return h[i].i > h[j].i}
func (h heapByID) Swap(i, j int)  {h[i], h[j] = h[j], h[i]}
func (h heapByID) Len() int {return len(h)}
func (h *heapByID) Push(x interface{}) {
    *h = append(*h, x.(pair))
}
func (h *heapByID) Pop() interface{} {
    old := *h
    n := len(old)
    x := old[n - 1]
    *h = old[:n - 1]
    return x
}


type heapByTime []pair
func (h heapByTime) Less(i, j int) bool {return h[i].t < h[j].t}
func (h heapByTime) Swap(i, j int)  {h[i], h[j] = h[j], h[i]}
func (h heapByTime) Len() int {return len(h)}
func (h *heapByTime) Push(x interface{}) {
    *h = append(*h, x.(pair))
}
func (h *heapByTime) Pop() interface{} {
    old := *h
    n := len(old)
    x := old[n - 1]
    *h = old[:n - 1]
    return x
}


func max(a, b int) int {
    if a > b {
        return a
    }
    return b
}

func min(a, b int) int {
    if a < b {
        return a
    }
    return b
}