LC-2532.过桥的时间

题目描述

leetcode 困难题

共有 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 + rightToLeftj
  • leftToRighti + rightToLefti == leftToRightj + rightToLeftj 且 i > j

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

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

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

示例1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
输入:n = 3, k = 2, time = [[1,9,1,8],[10,10,10,10]]
输出:50
解释:
0 10 :工人 1 从左岸过桥到达右岸。
10 20 :工人 1 从旧仓库搬起一个箱子。
10 11 :工人 0 从左岸过桥到达右岸。
11 20 :工人 0 从旧仓库搬起一个箱子。
20 30 :工人 1 从右岸过桥到达左岸。
30 40 :工人 1 将箱子放入新仓库。
30 31 :工人 0 从右岸过桥到达左岸。
31 39 :工人 0 将箱子放入新仓库。
39 40 :工人 0 从左岸过桥到达右岸。
40 49 :工人 0 从旧仓库搬起一个箱子。
49 50 :工人 0 从右岸过桥到达左岸。
50 58 :工人 0 将箱子放入新仓库。
整个过程在 58 分钟后结束。因为问题关注的是最后一个工人到达左岸的时间,所以返回 50

提示1:

1
2
3
4
1 <= n, k <= 10^4
time.length == k
time[i].length == 4
1 <= leftToRighti, pickOldi, rightToLefti, putNewi <= 1000

模拟

通过四个堆进行模拟,分别是:

  • $leftBridge$ 左边等桥的工人序号,堆顶为效率最小的工人。
  • $rightBridge$ 右边等桥的工人序号,堆顶为效率最小的工人。
  • $leftPuttingNew$ 左边正在搬箱子的工人,$int[0]$ 为该工人搬运当前箱子完成的时间,$int[1]$ 为工人序号,堆顶为搬运完成时间最早的工人。
  • $rightPickingOld$ 右边正在搬箱子的工人,$int[0]$ 为该工人搬运当前箱子完成的时间,$int[1]$ 为工人序号,堆顶为搬运完成时间最早的工人。

初始化当前时间为 $0$,模拟工人搬运过程,由于关注的是最后一个搬运箱子的工人到达左岸的时间,所以中止条件为箱子搬完且右岸不存在任何工人。

按照题意,每次循环时,如果右边等桥的人非空,右边等桥的堆顶工人出堆过桥,更新当前时间为过桥后的时间,并将工人放入左边搬运箱子的堆中;否则如果左边等桥的人非空且存在箱子未搬运,就将左边等桥的堆顶工人出堆过桥,更新当前时间为过桥后的时间,并将工人放入右边搬运箱子的堆中,箱子数量减一;如果以上两个条件均不满足,则说明当前时间过小,工人未搬完箱子,更新时间为左右岸搬完箱子的最小时间即可。

显然在上面的三个判断之前,还需要判断左右岸正在搬箱子的工人,如果已经搬完箱子了,就将其放入等桥的队列中。

循环结束时,当前时间为答案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class Solution {

public int findCrossingTime(int n, int k, int[][] time) {
Comparator<Integer> cmp = (a, b) -> {
int d = (time[b][0] + time[b][2]) - (time[a][0] + time[a][2]);
return d != 0 ? d : b - a;
};
PriorityQueue<Integer> leftBridge = new PriorityQueue<>(cmp);
PriorityQueue<Integer> rightBridge = new PriorityQueue<>(cmp);
PriorityQueue<int[]> leftPuttingNew = new PriorityQueue<>(Comparator.comparingInt(a -> a[0]));
PriorityQueue<int[]> rightPickingOld = new PriorityQueue<>(Comparator.comparingInt(a -> a[0]));
leftBridge.addAll(IntStream.range(0, k).boxed().toList());
int curTime = 0;
while (n + rightPickingOld.size() + rightBridge.size() > 0) {
while (!rightPickingOld.isEmpty() && rightPickingOld.peek()[0] <= curTime) {
rightBridge.offer(rightPickingOld.poll()[1]);
}
while (!leftPuttingNew.isEmpty() && leftPuttingNew.peek()[0] <= curTime) {
leftBridge.offer(leftPuttingNew.poll()[1]);
}
if (!rightBridge.isEmpty()) {
int worker = Objects.requireNonNull(rightBridge.poll());
curTime += time[worker][2];
leftPuttingNew.offer(new int[]{curTime + time[worker][3], worker});
} else if (n > 0 && !leftBridge.isEmpty()) {
int worker = Objects.requireNonNull(leftBridge.poll());
curTime += time[worker][0];
rightPickingOld.offer(new int[]{curTime + time[worker][1], worker});
n--;
} else {
curTime = Math.min(
rightPickingOld.isEmpty() ? Integer.MAX_VALUE : rightPickingOld.peek()[0],
leftPuttingNew.isEmpty() ? Integer.MAX_VALUE : leftPuttingNew.peek()[0]);
}
}
return curTime;
}

}

LC-2532.过桥的时间
https://wecgwm.github.io/2023/01/11/LC-2532-过桥的时间/
作者
yichen
发布于
2023年1月11日
许可协议