30. Min Agents for Overlap
Problem
Human agents pick up live conversations that Fin escalates during a shift. Given
each escalated conversation's (start, end) time on the shift's
timeline, compute the minimum number of human agents needed working simultaneously
to handle every escalation, without any agent working two conversations at once.
Source: src/30_min_agents_for_overlap.py
def min_agents_needed(intervals: list[tuple[int, int]]) -> int: ...
min_agents_needed([(0, 30), (5, 10), (15, 20)])
# -> 2 ((5,10) and (15,20) both overlap (0,30), but not each other,
# so at most 2 conversations are ever live at once)
min_agents_needed([(1, 5), (5, 10)])
# -> 1 (back-to-back, not overlapping -- one agent can handle both)
Step-by-Step Approach
- If
intervalsis empty, return 0 immediately. - Split the intervals into two separate sorted lists:
starts, all start times sorted ascending, andends, all end times sorted ascending. Note these lists are no longer paired by original interval — that's fine, only the sorted order matters. - Initialize
agents = 0(agents currently busy),max_agents = 0(the running maximum, which is the answer), and two pointerss_ptr,e_ptrboth starting at 0. - Sweep forward while
s_ptr < n(there are still starts to process). At each step, compare the next unprocessed start time against the next unprocessed end time. - If
starts[s_ptr] < ends[e_ptr], a new conversation begins before the earliest currently-busy agent frees up: incrementagents, updatemax_agentsif this is a new high, and advances_ptr. - Otherwise, the earliest busy agent frees up at or before the next start:
decrement
agentsand advancee_ptr, without consuming a start. - When the sweep finishes,
max_agentsis the peak number of simultaneously overlapping conversations — the minimum number of agents needed.
The key insight is that you don't need to track which interval belongs to which
agent — only how many are open at once — so merging all starts and ends into two
sorted streams and sweeping them together (like merging two sorted lists) finds the
peak concurrency in O(n log n) instead of checking every pair of
intervals for overlap.
Reference solution
def min_agents_needed(intervals: list[tuple[int, int]]) -> int:
# sort starts and ends separately, two-pointer sweep, O(n log n) time, O(n) space
if not intervals:
return 0
# separate sorted arrays, pairing discarded
starts = sorted(start for start, _ in intervals)
ends = sorted(end for _, end in intervals)
agents = 0
max_agents = 0
s_ptr = e_ptr = 0
n = len(intervals)
while s_ptr < n:
# strict <: same-time end frees agent first
if starts[s_ptr] < ends[e_ptr]:
agents += 1
# track running peak
max_agents = max(max_agents, agents)
s_ptr += 1
else:
# earliest busy agent frees up
agents -= 1
e_ptr += 1
return max_agents
Key Functions & Tricks
sorted(start for start, _ in intervals)— builds two separate sorted arrays, discarding the original pairing.- Two-pointer sweep — walks
starts/endsindependently, like a merge-sort merge step. max_agents = max(max_agents, agents)— tracks the running peak on start events only.starts[s_ptr] < ends[e_ptr]— strict<so a same-time end frees an agent before a new start needs one.
How to Recognize This Pattern
Signals: "minimum number of resources/rooms/agents to handle all overlapping intervals" is the classic Meeting Rooms II tell. More generally, any "peak concurrency" question over a set of start/end intervals (max simultaneous users, max simultaneous connections) reduces to the same sweep.
Variations: instead of returning the count, return the actual assignment of each interval to a specific agent/room, which typically uses a min-heap keyed by end-time instead of two separate sorted arrays; or answer "can this be done with exactly k agents" as a yes/no check against the same peak-concurrency computation.
Common pitfall: using <= instead of < (or vice
versa) when comparing a start to an end — this problem treats an interval ending
exactly when another starts as non-overlapping (one agent can handle
back-to-back), so ties must favor processing the end first when a start and end
coincide.