Maximum Profit in Job Scheduling

The problem description can be found in LeetCode.

Approach

An easy way to think about this problem would be reason on how we should select or discard a certain job.

What if we try every possibility?

We can either select the first job or discard it. Then, for both choices, we can again select the second job or discard it.

This decision tree and a possible repeating work in checking this options suggest to apply Dynamic Programming.

However, we need an efficient way for selecting the next job that we might select or discard. Usually, for these kind of problems, sorting the input is highly recommended, since any other brute force approach would have surely a higher complexity compared to .

Again, sorting the input suggests that Binary Search might be used. In fact, in order to select the next job, we can use it to quickly find it.

Solution

import bisect
from typing import Dict, List


class Solution:
    def jobScheduling(
        self, startTimes: List[int], endTimes: List[int], profits: List[int]
    ) -> int:
        def dfs(i: int, memo: Dict[int, int]) -> int:
            if i >= len(startTimes):
                return 0
            if i in memo:
                return memo[i]

            next_pos = bisect.bisect_left(startTimes, endTimes[i])
            profit = max(
                dfs(i + 1, memo),
                profits[i] + dfs(next_pos, memo),
            )

            memo[i] = profit

            return memo[i]

        # sort the lists based on the `startTimes` one
        combined = list(zip(startTimes, endTimes, profits))
        sorted_combined = sorted(combined, key=lambda lst: lst[0])
        startTimes = [v[0] for v in sorted_combined]
        endTimes = [v[1] for v in sorted_combined]
        profits = [v[2] for v in sorted_combined]

        return dfs(0, {})

Complexity

  • Time:
  • Space:

Where is the size of the three input lists.