题目:
共有 k 位工人计划将 n 个箱子从旧仓库移动到新仓库。给你两个整数 n 和 k,以及一个二维整数数组 time ,数组的大小为 k x 4 ,其中 time[i] = [leftToRighti, pickOldi, rightToLefti, putNewi] 。
一条河将两座仓库分隔,只能通过一座桥通行。旧仓库位于河的右岸,新仓库在河的左岸。开始时,所有 k 位工人都在桥的左侧等待。为了移动这些箱子,第 i 位工人(下标从 0 开始)可以:
- 从左岸(新仓库)跨过桥到右岸(旧仓库),用时
leftToRighti分钟。 - 从旧仓库选择一个箱子,并返回到桥边,用时
pickOldi分钟。不同工人可以同时搬起所选的箱子。 - 从右岸(旧仓库)跨过桥到左岸(新仓库),用时
rightToLefti分钟。 - 将箱子放入新仓库,并返回到桥边,用时
putNewi分钟。不同工人可以同时放下所选的箱子。
如果满足下面任一条件,则认为工人 i 的 效率低于 工人 j :
leftToRighti + rightToLefti > leftToRightj + rightToLeftjleftToRighti + rightToLefti == leftToRightj + rightToLeftj且i > 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
}