Time to Cross a Bridge
HardPrompt
There are k workers who want to move n boxes from the right (old) warehouse to the left (new) warehouse. You are given the two integers n and k, and a 2D integer array time of size k x 4 where time[i] = [righti, picki, lefti, puti].
The warehouses are separated by a river and connected by a bridge. Initially, all k workers are waiting on the left side of the bridge. To move the boxes, the ith worker can do the following:
- Cross the bridge to the right side in
rightiminutes. - Pick a box from the right warehouse in
pickiminutes. - Cross the bridge to the left side in
leftiminutes. - Put the box into the left warehouse in
putiminutes.
The ith worker is less efficient than the jth worker if either condition is met:
lefti + righti > leftj + rightjlefti + righti == leftj + rightjandi > j
The following rules regulate the movement of the workers through the bridge:
- Only one worker can use the bridge at a time.
- When the bridge is unused prioritize the least efficient worker (who have picked up the box) on the right side to cross. If not, prioritize the least efficient worker on the left side to cross.
- If enough workers have already been dispatched from the left side to pick up all the remaining boxes, no more workers will be sent from the left side.
Return the elapsed minutes at which the last box reaches the left side of the bridge.
Example 1:
Input: n = 1, k = 3, time = [[1,1,2,1],[1,1,3,1],[1,1,4,1]]
Output: 6
Explanation:
From 0 to 1 minutes: worker 2 crosses the bridge to the right.
From 1 to 2 minutes: worker 2 picks up a box from the right warehouse.
From 2 to 6 minutes: worker 2 crosses the bridge to the left.
From 6 to 7 minutes: worker 2 puts a box at the left warehouse.
The whole process ends after 7 minutes. We return 6 because the problem asks for the instance of time at which the last worker reaches the left side of the bridge.Example 2:
Input: n = 3, k = 2, time = [[1,5,1,8],[10,10,10,10]]
Output: 37
Explanation:
The last box reaches the left side at 37 seconds. Notice, how we do not put the last boxes down, as that would take more time, and they are already on the left with the workers.
Constraints:
1 <= n, k <= 104time.length == ktime[i].length == 41 <= lefti, picki, righti, puti <= 1000
Approaches
2 approaches with complexity analysis and trade-offs.
This approach simulates the process by incrementing time one unit at a time. At each time step, it checks the status of all workers and the bridge to see if any state changes occur. This method is straightforward to conceptualize but is computationally expensive.
Algorithm
- Initialize
currentTime = 0, worker states (location, task, finish time),boxes_to_move = n,boxes_delivered = 0. - Create lists or arrays to represent workers waiting on the left and right sides.
- Start a loop that continues as long as
boxes_delivered < n. - Inside the loop, for the current
currentTime: a. Iterate through allkworkers. If a worker's task was scheduled to finish atcurrentTime, update their status (e.g., from 'picking' to 'waiting on right'). b. Check if the bridge is free (i.e.,currentTimeis greater than or equal to the time the last crossing ends). c. If the bridge is free, determine who crosses next: i. Check the list of workers waiting on the right. If not empty, find the one with the highest priority (least efficient) and dispatch them. ii. If no one from the right crosses, check the list of workers waiting on the left. If not empty and more boxes need to be moved, find the highest priority worker and dispatch them. iii. When a worker is dispatched, update their state to 'crossing', calculate their arrival time, and set the bridge's busy-until time. d. IncrementcurrentTimeby 1. - The simulation ends when the
n-th box reaches the left side of the bridge. Return thecurrentTimeof that event.
Walkthrough
In this method, we maintain the state of each worker, including their location (left/right), their current task (waiting, crossing, picking, putting), and the time they will finish their current task. We also track the state of the bridge (busy/free) and until when it's busy. The simulation proceeds in a loop, incrementing a currentTime variable from 0 upwards. In each iteration, we check all workers to see if they have completed their tasks. If the bridge is free, we apply the given priority rules to select a worker from the waiting queues (first from the right, then from the left). The selected worker begins crossing, and their state is updated accordingly. This continues until n boxes have reached the left side of the bridge.
Complexity
Time
O(T * k * log k), where `T` is the final completion time. `T` can be very large (e.g., `n` times the average cycle time). At each time step, we might need to iterate through and sort the `k` workers, making this approach too slow for the given constraints.
Space
O(k), to store the state of each of the `k` workers.
Trade-offs
Pros
Conceptually simple and easy to understand.
Cons
Extremely inefficient for the given constraints. The total simulation time can be very large, leading to a 'Time Limit Exceeded' error.
The complexity is proportional to the total elapsed time, which is not ideal for problems with large time values.
Solutions
Solution
class Solution {public int findCrossingTime(int n, int k, int[][] time) { int[][] t = new int[k][5]; for (int i = 0; i < k; ++i) { int[] x = time[i]; t[i] = new int[]{x[0], x[1], x[2], x[3], i}; } Arrays.sort( t, (a, b)->{ int x = a[0] + a[2], y = b[0] + b[2]; return x == y ? a[4] - b[4] : x - y; }); int cur = 0; PriorityQueue<Integer> waitInLeft = new PriorityQueue<>((a, b)->b - a); PriorityQueue<Integer> waitInRight = new PriorityQueue<>((a, b)->b - a); PriorityQueue<int[]> workInLeft = new PriorityQueue<>((a, b)->a[0] - b[0]); PriorityQueue<int[]> workInRight = new PriorityQueue<>((a, b)->a[0] - b[0]); for (int i = 0; i < k; ++i) { waitInLeft.offer(i); } while (true) { while (!workInLeft.isEmpty()) { int[] p = workInLeft.peek(); if (p[0] > cur) { break; } waitInLeft.offer(workInLeft.poll()[1]); } while (!workInRight.isEmpty()) { int[] p = workInRight.peek(); if (p[0] > cur) { break; } waitInRight.offer(workInRight.poll()[1]); } boolean leftToGo = n > 0 && !waitInLeft.isEmpty(); boolean rightToGo = !waitInRight.isEmpty(); if (!leftToGo && !rightToGo) { int nxt = 1 << 30; if (!workInLeft.isEmpty()) { nxt = Math.min(nxt, workInLeft.peek()[0]); } if (!workInRight.isEmpty()) { nxt = Math.min(nxt, workInRight.peek()[0]); } cur = nxt; continue; } if (rightToGo) { int i = waitInRight.poll(); cur += t[i][2]; if (n == 0 && waitInRight.isEmpty() && workInRight.isEmpty()) { return cur; } workInLeft.offer(new int[]{cur + t[i][3], i}); } else { int i = waitInLeft.poll(); cur += t[i][0]; --n; workInRight.offer(new int[]{cur + t[i][1], i}); } } }}Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.