PracHub
QuestionsPremiumLearningGuidesInterview PrepNEWCoaches

Quick Overview

This question evaluates understanding of weighted interval scheduling, dynamic programming, and efficient algorithm design for large-scale inputs, as well as reasoning about correctness, complexity, and edge cases like identical times or zero-length jobs.

  • Medium
  • Airbnb
  • Coding & Algorithms
  • Software Engineer

Maximize reward by scheduling jobs

Company: Airbnb

Role: Software Engineer

Category: Coding & Algorithms

Difficulty: Medium

Interview Round: Technical Screen

Given n jobs, each with a start time, end time, and reward, choose a subset of non-overlapping jobs to maximize total reward. Return the maximum reward and one optimal set of job indices. Constraints: n up to 100,000; times within 32-bit or 64-bit integers; rewards non-negative. Aim for O(n log n) time and near O(n) space. Explain your algorithm (e.g., sorting, binary search, and DP), prove correctness, analyze complexity, and discuss edge cases such as identical start/end times and zero-length jobs.

Quick Answer: This question evaluates understanding of weighted interval scheduling, dynamic programming, and efficient algorithm design for large-scale inputs, as well as reasoning about correctness, complexity, and edge cases like identical times or zero-length jobs.

You are given an array `jobs` where `jobs[i] = (start_i, end_i, reward_i)`. Choose a subset of jobs with maximum total reward such that no two chosen jobs overlap. Treat each job as a half-open interval `[start_i, end_i)`, so a job ending at time `t` does not overlap a job starting at time `t`. Zero-length jobs with `start_i == end_i` are allowed. Return both the maximum reward and one optimal set of original 0-based job indices in the order they are scheduled. If multiple optimal answers exist, you may return any one of them. Your solution should aim for `O(n log n)` time.

Constraints

  • `0 <= n <= 100000`
  • `jobs[i] = (start_i, end_i, reward_i)`
  • `-2^63 <= start_i, end_i <= 2^63 - 1`
  • `start_i <= end_i`
  • `0 <= reward_i <= 10^9`

Examples

Input: ([],)

Expected Output: (0, [])

Explanation: There are no jobs to take, so the maximum reward is 0 and the chosen index list is empty.

Input: ([(2, 5, 10)],)

Expected Output: (10, [0])

Explanation: With only one job, the best choice is to take it.

Input: ([(1, 3, 50), (3, 5, 20), (6, 19, 100), (2, 100, 200)],)

Expected Output: (200, [3])

Explanation: Taking job 3 alone yields reward 200, which is better than taking jobs 0, 1, and 2 for a total of 170.

Input: ([(1, 1, 5), (1, 2, 4), (2, 2, 3), (2, 3, 6)],)

Expected Output: (18, [0, 1, 2, 3])

Explanation: Under half-open interval rules, jobs that touch at endpoints do not overlap, and zero-length jobs can be included as well.

Input: ([(1, 4, 5), (1, 4, 7), (4, 6, 4), (6, 7, 3)],)

Expected Output: (14, [1, 2, 3])

Explanation: Jobs 0 and 1 overlap completely, so choose the more rewarding one (job 1), then chain jobs 2 and 3.

Solution

def solution(jobs):
    """
    Weighted interval scheduling in O(n log n) time.

    Algorithm:
    1. Attach original indices to each job and sort by end time.
    2. For each sorted job i, binary-search the last earlier job p[i]
       whose end time is <= the current job's start time.
    3. Let dp[i] be the maximum reward obtainable using the first i sorted jobs.
       Then the recurrence is:
           dp[i] = max(dp[i - 1], reward_i + dp[p[i]])
       because an optimal solution on the first i jobs either skips job i,
       or takes it and combines it with the best compatible prefix.
    4. Record which jobs were taken and backtrack to reconstruct one optimal
       set of original indices.

    Correctness sketch:
    After sorting by end time, every job compatible with sorted job i lies in
    the prefix ending at p[i]. Any optimal solution among the first i jobs must
    be in exactly one of two cases:
      - it excludes job i, giving value dp[i - 1], or
      - it includes job i, in which case the remaining chosen jobs must form an
        optimal solution among the first p[i] jobs, giving
        reward_i + dp[p[i]].
    Taking the maximum of these two values therefore yields the optimal value
    for the first i jobs. By induction over i, dp[n] is optimal for all jobs.
    The backtracking step follows the same decisions, so it reconstructs a
    schedule whose reward is exactly dp[n].

    Edge cases handled:
    - Empty input returns (0, []).
    - Identical start/end times are handled naturally by sorting and binary search.
    - Zero-length jobs (start == end) are valid. Because intervals are treated
      as [start, end), jobs with end == next_start are compatible, including
      multiple zero-length jobs at the same time.
    """
    from bisect import bisect_right

    n = len(jobs)
    if n == 0:
        return (0, [])

    ordered = [(start, end, reward, idx) for idx, (start, end, reward) in enumerate(jobs)]
    ordered.sort(key=lambda job: (job[1], job[0], job[3]))

    ends = [job[1] for job in ordered]

    # p[i] = number of jobs in the sorted prefix that end <= start time of job i
    # Using 1-based DP indexing, this is exactly the DP state we should jump to.
    p = [0] * (n + 1)
    for i, (start, end, reward, idx) in enumerate(ordered, 1):
        p[i] = bisect_right(ends, start, 0, i - 1)

    dp = [0] * (n + 1)
    take = [False] * (n + 1)

    for i in range(1, n + 1):
        reward = ordered[i - 1][2]
        include = reward + dp[p[i]]
        exclude = dp[i - 1]

        if include > exclude:
            dp[i] = include
            take[i] = True
        else:
            dp[i] = exclude

    chosen = []
    i = n
    while i > 0:
        if take[i]:
            chosen.append(ordered[i - 1][3])
            i = p[i]
        else:
            i -= 1

    chosen.reverse()
    return (dp[n], chosen)

Time complexity: O(n log n). Space complexity: O(n).

Hints

  1. Sort jobs by end time so that when you process a job, all potentially compatible earlier jobs are in a prefix.
  2. For each job, use binary search to find the last job whose end time is less than or equal to the current job's start time, then use dynamic programming to choose between taking or skipping the job.
Last updated: Apr 22, 2026

Loading coding console...

PracHub

Master your tech interviews with 7,500+ real questions from top companies.

Product

  • Questions
  • Learning Tracks
  • Interview Guides
  • Resources
  • Premium
  • For Universities
  • Student Access

Browse

  • By Company
  • By Role
  • By Category
  • Topic Hubs
  • SQL Questions
  • Compare Platforms
  • Discord Community

Support

  • support@prachub.com
  • (916) 541-4762

Legal

  • Privacy Policy
  • Terms of Service
  • About Us

© 2026 PracHub. All rights reserved.

Related Coding Questions

  • Determine Exact Layover Booking - Airbnb (medium)
  • Solve Linked-List and Iterator Problems - Airbnb
  • Implement Text Layout and Query Parsing - Airbnb (easy)
  • Parse Query Parameters Into a Map - Airbnb (medium)
  • Find smallest permutation under constraints - Airbnb (medium)