Assign tennis bookings to minimum courts
Company: Atlassian
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Onsite
Quick Answer: This question evaluates understanding of interval scheduling and temporal resource allocation, testing algorithm design, complexity analysis, and appropriate data-structure use for assigning bookings to a minimal number of courts.
Expanding Tennis Club - Part (a): Minimum Courts
Constraints
- 0 <= number of bookings
- For each booking, start < finish; times are integers
- Bookings touching at an endpoint (finish == next start) do not conflict
- An unlimited number of courts is available; minimize the count actually used
Examples
Input: ([[1, 0, 30], [2, 5, 10], [3, 15, 20]],)
Expected Output: [[1, 0], [2, 1], [3, 1]]
Explanation: Booking 1 spans the whole window so it holds court 0. Booking 2 needs a second court (1). Booking 2 frees court 1 at time 10, so booking 3 (starts 15) reuses court 1. Two courts.
Input: ([[1, 1, 10], [2, 2, 7], [3, 3, 19], [4, 8, 12], [5, 10, 20], [6, 11, 30]],)
Expected Output: [[1, 0], [2, 1], [3, 2], [4, 1], [5, 0], [6, 3]]
Explanation: Peak overlap is 4 (around time 11, bookings 3,4,5,6 plus releases), so four courts (0..3) are used. Court 1 frees at 7 and is reused by booking 4; court 0 frees at 10 and is reused by booking 5.
Input: ([],)
Expected Output: []
Explanation: No bookings -> empty assignment.
Input: ([[7, 100, 200]],)
Expected Output: [[7, 0]]
Explanation: A single booking always takes court 0.
Input: ([[1, 0, 10], [2, 10, 20], [3, 20, 30]],)
Expected Output: [[1, 0], [2, 0], [3, 0]]
Explanation: Back-to-back bookings touch only at endpoints, so one court serves all three.
Input: ([[1, 0, 5], [2, 0, 5], [3, 0, 5]],)
Expected Output: [[1, 0], [2, 1], [3, 2]]
Explanation: Three fully simultaneous bookings force three separate courts.
Hints
- The minimum number of courts equals the maximum number of bookings overlapping at any single instant -- this is the classic 'minimum meeting rooms' result.
- Sort the events by start time; keep a min-heap of (finishTime, courtId) for courts in use so you can cheaply find the court that frees up earliest.
- To make the assignment deterministic, free every court whose finish <= the current start, then reuse the smallest available courtId, opening a new court only when none is free.
Expanding Tennis Club - Part (b): Maintenance Buffer After Each Booking
Constraints
- 0 <= number of bookings
- For each booking, start < finish; times are integers
- x >= 0 (the maintenance buffer; x == 0 is equivalent to Part (a))
- After a booking finishing at t, the next booking on that court must start at >= t + x
Examples
Input: ([[1, 0, 10], [2, 10, 20], [3, 20, 30]], 0)
Expected Output: [[1, 0], [2, 0], [3, 0]]
Explanation: With no buffer, back-to-back bookings share one court (same as Part (a)).
Input: ([[1, 0, 10], [2, 10, 20], [3, 20, 30]], 5)
Expected Output: [[1, 0], [2, 1], [3, 0]]
Explanation: Booking 1 finishes at 10 but court 0 is in maintenance until 15, so booking 2 (start 10) needs court 1. Court 0 is free again by time 20, so booking 3 reuses it.
Input: ([[1, 0, 10], [2, 15, 20], [3, 20, 30]], 5)
Expected Output: [[1, 0], [2, 0], [3, 1]]
Explanation: Court 0 frees at 10+5=15, exactly when booking 2 starts, so it is reused. Court 0 then frees at 20+5=25; booking 3 (start 20) cannot wait, so it takes court 1.
Input: ([], 3)
Expected Output: []
Explanation: No bookings -> empty assignment.
Input: ([[9, 0, 100]], 10)
Expected Output: [[9, 0]]
Explanation: A single booking takes court 0; the buffer is irrelevant with nothing after it.
Input: ([[1, 0, 5], [2, 0, 5]], 100)
Expected Output: [[1, 0], [2, 1]]
Explanation: Two simultaneous bookings always need two courts regardless of the buffer.
Input: ([[1, 0, 10], [2, 12, 20], [3, 12, 20]], 2)
Expected Output: [[1, 0], [2, 0], [3, 1]]
Explanation: Court 0 frees at 10+2=12, so booking 2 (start 12) reuses it; the simultaneous booking 3 must open court 1.
Hints
- The only change from Part (a) is when a court becomes reusable: treat its availability time as finish + X instead of finish.
- Push (finish + X, courtId) onto the in-use heap; a court is releasable for a new booking only when finish + X <= that booking's start.
- With X = 0 the algorithm must reproduce Part (a) exactly -- a good sanity check.
Expanding Tennis Club - Part (c): Maintenance Only After Accumulated Usage
Constraints
- 0 <= number of bookings
- For each booking, start < finish; times are integers
- x >= 0 (maintenance buffer), t >= 1 (usage-time threshold)
- Threshold is interpreted as TOTAL accumulated used time on a court; the counter resets to 0 after each maintenance
- A very large t means maintenance effectively never fires (reduces toward Part (a))
Examples
Input: ([[1, 0, 10], [2, 10, 20], [3, 20, 30]], 5, 1000)
Expected Output: [[1, 0], [2, 0], [3, 0]]
Explanation: Threshold 1000 is never reached, so no maintenance fires and one court serves all back-to-back bookings.
Input: ([[1, 0, 10], [2, 10, 20], [3, 20, 30]], 5, 10)
Expected Output: [[1, 0], [2, 1], [3, 0]]
Explanation: Booking 1 uses 10 >= T, so court 0 enters maintenance until 15; booking 2 (start 10) opens court 1 (also hits 10 >= T, maint until 25). Court 0 is free by 20, reused by booking 3.
Input: ([[1, 0, 6], [2, 6, 12], [3, 12, 18], [4, 18, 24]], 5, 10)
Expected Output: [[1, 0], [2, 0], [3, 1], [4, 0]]
Explanation: Court 0 usage after b1=6 (<10, free at 6) so b2 reuses it; after b2 usage=12 (>=10) -> maintenance until 17, counter resets. b3 (start 12) cannot use court 0, opens court 1. Court 0 frees at 17, so b4 (start 18) reuses court 0.
Input: ([], 5, 10)
Expected Output: []
Explanation: No bookings -> empty assignment.
Input: ([[9, 0, 100]], 10, 50)
Expected Output: [[9, 0]]
Explanation: A single booking takes court 0; its maintenance afterward never matters.
Input: ([[1, 0, 5], [2, 0, 5]], 100, 1)
Expected Output: [[1, 0], [2, 1]]
Explanation: Simultaneous bookings need two courts; with T=1 each immediately triggers maintenance, but that does not change the overlap requirement.
Input: ([[1, 0, 10], [2, 10, 20], [3, 22, 30], [4, 22, 30]], 5, 10)
Expected Output: [[1, 0], [2, 1], [3, 0], [4, 2]]
Explanation: b1 fills court 0 (usage 10 -> maint to 15, reset). b2 opens court 1 (maint to 25, reset). At time 22 court 0 is free so b3 reuses it (usage 8 < 10, free at 30); court 1 still in maintenance until 25, so simultaneous b4 opens court 2.
Hints
- Track per-court accumulated usage; only when it crosses T do you delay the court's next availability by X and reset the counter.
- Keep the same min-heap of (availableTime, courtId), but compute availableTime as finish + X when usage >= T, otherwise just finish.
- Edge behavior: with T larger than any possible accumulated usage the court is always free at finish (Part (a)); with T = 1 every nonzero-duration booking triggers maintenance (Part (b)).