How it works
A greedy algorithm can be correct when the problem supports a specific exchange argument. For maximum-count interval scheduling, choosing the available interval with the earliest finishing time leaves as much room as possible for later intervals. The sample sorts by finish time and accepts each compatible job.
B ends at two, D starts at two and ends at four, and E starts at four. All three are selected. The comparison permits a job to begin exactly when the previous one ends, matching a half-open scheduling interpretation. A different adjacency rule would require a different condition and potentially a different result.
The objective is the number of accepted jobs. It is not maximum total duration, revenue, or priority. If jobs have different values, earliest-finish greedy selection can be wrong for that weighted objective. Define the optimization target before choosing an algorithm whose familiar name looks applicable.
The input contract requires positive durations, and the sample rejects reversed or empty intervals. Tie ordering by identifier makes the selected representative deterministic when finish times match. The tie rule supports reproducible output, while the greedy correctness reasoning concerns the finish-time choice and compatibility policy.
Practice by adding a long job covering the whole schedule and observing that selecting it first would reduce the count. Then add two equal-finish candidates and inspect the identifier tie breaker. Test an empty input and jobs that merely touch at endpoints.
Finally, write a brute-force subset checker for a tiny set of jobs and compare its maximum compatible count with the greedy result. That reference test can catch implementation mistakes, while the exchange argument explains why the strategy generalizes. Keep weighted scheduling as a separate problem rather than stretching this helper beyond the objective it was designed to solve.
A working example
using System;
using System.Linq;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.CompilerServices;
var jobs = new[] { new Job("A", 0, 3), new Job("B", 1, 2), new Job("C", 3, 5), new Job("D", 2, 4), new Job("E", 4, 6) };
var chosen = new List<string>(); int end = int.MinValue;
foreach (var job in jobs.OrderBy(job => job.End).ThenBy(job => job.Id, StringComparer.Ordinal)) {
if (job.Start >= job.End) throw new ArgumentException("Positive duration required");
if (job.Start >= end) { chosen.Add(job.Id); end = job.End; }
}
Console.WriteLine(string.Join(",", chosen));
record Job(string Id, int Start, int End);
Expected output
B,D,E
This problem maximizes count, not total value, and permits one job to start when another ends.