Intervals on a line produce interval graphs, which are perfect: maximum clique = chromatic number, and maximum independent set = minimum clique cover. Most of these problems fall to a sort plus a sweep.

Conventions first

Decide whether touching intervals conflict. With half-open they don’t; with closed they do. In event sweeps this becomes the tie-break: process “end” () before “start” () at equal times for half-open intervals. Getting it backwards fails exactly one test.

Selection

Maximum number of disjoint intervals: sort by end and take every interval that starts after the last taken end. Greedy stays ahead. Earliest start, shortest length and fewest conflicts are all wrong rules.

Why the other sort keys fail

  • Earliest start — one very long interval starting first blocks everything after it.
  • Shortest duration — a short interval in the middle can block two long, disjoint ones on either side.
  • Fewest conflicts — plausible-sounding, but small constructible counterexamples exist.

Only earliest finish time provably stays ahead of every other schedule at every step.

sort(all(iv), [](auto& a, auto& b) { return a.second < b.second; });
int cnt = 0; ll last = LLONG_MIN;
for (auto [s, e] : iv) if (s >= last) cnt++, last = e;     // half-open
  • Minimum intervals to remove so the rest are disjoint: the above.
  • Minimum points stabbing all intervals has the same answer (duality): put a point at each chosen end.
  • At most intervals overlapping at any point, maximise the count: sort by end, keep a multiset of the machines’ free times, assign each interval to the machine that freed latest before its start, otherwise skip it.
  • On a circle: fix the interval with the earliest end that you take, then solve linearly; or double the time axis and use binary lifting on “next interval” pointers to get for all starting choices.
  • Many queries “max disjoint intervals inside ”: precompute = interval with the smallest end among those starting at or after the end of . Then binary lifting answers each query in .

Weighted (maximise total value): DP over intervals sorted by end, with = the last interval ending at or before :

Binary search for , .

The DP is necessary (greedy fails) because the best choice at interval depends on the best achievable before ‘s start, by value or by value density — information greedy’s single forward pass never reconstructs; one huge-value interval can also simply outweigh several smaller ones it conflicts with, so greedy-by-count is wrong too.

With parallel machines: min-cost flow on the time axis (a chain with capacity and cost 0, plus an arc of capacity 1 and cost from to for each interval), . That modelling pattern, “intervals as arcs over a time line with capacity ”, is reusable.

Further variants of the weighted problem:

VariantMethodCost
at most intervalsadd a second DP dimension, dp[i][k]
intervals on a circle, weightedfix one interval as forced-included / forced-excluded, solve linearly both ways
setup time needed between consecutive intervalssearch for against instead of
maximise count, tie-break by weightDP on the pair (count, weight)
value depends on when it’s scheduled, not just which intervala more general DP keyed on position, not just indexproblem-specific

The recurring shape — sort by one coordinate, binary search (or two-pointer) the last compatible predecessor, take the max — generalises well beyond intervals:

ProblemThe “predecessor”
weighted interval schedulinglast interval finishing before this one starts
LIS with weightslast smaller element
longest chain of pairslast compatible pair
box stacking / Russian dollslast box this one fits inside
job sequencing with deadlinessee Scheduling
maximum sum of non-adjacent elementsindex

When the predecessor index is monotone in , the binary search becomes a two-pointer, giving after the sort instead of .

Partitioning

Minimum rooms = maximum depth (number of intervals covering one point). Assign real rooms by sorting by start and reusing the room whose last interval ended earliest (a min-heap of (end, room)).

vector<pair<ll, int>> ev;
for (auto [s, e] : iv) ev.push_back({s, +1}), ev.push_back({e, -1});
sort(all(ev));                        // -1 before +1 at equal times: half-open
int cur = 0, depth = 0;
for (auto [t, d] : ev) depth = max(depth, cur += d);
  • Setup gap between consecutive bookings: extend each end by .
  • Depth at every point: difference array over compressed coordinates.
  • Which intervals contain a given query point: offline, a sweep; online, a set of currently-open intervals ordered by end, or a segment tree keyed by start.
  • Rooms with capacities or types: flow, or greedy with a heap per type when the types are nested.
  • CPU scheduling with cores: with fixed start and end times this is exactly minimum-rooms; once start times become flexible (choose when to run each job) it turns into a makespan problem, which is NP-hard — flexibility in timing is what breaks the interval-graph structure.

“Minimise the number of resources” and “maximise simultaneous demand” are dual, and equal, specifically because intervals form a perfect graph (clique number = chromatic number) — that equality is what makes a single sweep sufficient here. Outside interval structure the two quantities can differ and the problem usually becomes NP-hard, so spotting “this is an interval graph” is most of the insight.

Covering

Fewest intervals covering : sort by start; from the current covered point, among intervals starting at or before it, take the one reaching furthest; repeat. If none extends coverage, it’s impossible. For many queries, binary lifting on “furthest reach from ” gives each.

Union length / merging: sort by start and extend the current block while the next start is at most its end.

Point coverage queries: offline, sweep with a BIT over compressed endpoints; online, a segment tree or “merge sort tree on starts”.

Interval graph algorithms

ProblemPolynomial on interval graphs
maximum independent setgreedy by end
maximum cliquemaximum depth (sweep)
chromatic numbermaximum depth; greedy colouring in order of start
minimum clique coverequals max independent set
maximum weight independent setDP above
minimum dominating setgreedy
Hamiltonian path / cyclepolynomial, greedy-ish with care

General recognition and the chordal-graph extensions: Special Graph Classes.

Nesting and containment

  • Count, for each interval, how many intervals it contains / is contained in: sort by and count with a BIT over compressed (CSES “Nested Ranges Count”). Equal intervals contain each other, so handle duplicates with the tie order.
  • Longest chain of nested intervals: sort by ascending and descending, then the longest non-increasing subsequence of .
  • Interval containment as 2D dominance: contains iff and , so these are offline 2D counting problems (Offline 2D Queries).