LeetCode-Problems / 1235. Maximum Profit in Job Scheduling / 1235. Maximum Profit in Job Scheduling.py
1235. Maximum Profit in Job Scheduling.py
Raw
# 11-25-2022 Leetcode 1235. Maximum Profit in Job Scheduling
# https://leetcode.com/problems/maximum-profit-in-job-scheduling/

# First off, note there can be up to 50,000 entries in the jobs/profit
# lists. This automatically precludes brute force solutions like "generate
# all valid orders and compare profits", not that this is ever what
# leetcode solutions are looking for. If done recursively wed hit the stack

# Ok, well we have to start the day somehow. There will always be
# some job/s that start first. Oh wait! Does this kind of look like that
# monotonic stack problem? 907. Sum of Subarray Minimums
# https://leetcode.com/problems/sum-of-subarray-minimums/

# We could include the idea of rates: For each hour we can earn
# a rate, which is the jobs total/hours. This isnt enough to give
# a solution, but may be a component.


# Ok, didnt really know how to start and once again had to read
# through the answer. I rather like the last option, and it seems
# so simple that I should have been able to come up with it.
# keep a list of "chains" of current jobs. For each new job, add
# it to the chain that would be most profitable. We only need to compare
# chains that dont conflict: chains that are "ongoing" cannot include this
# new job so we can ignore them. All the chains that are "done" at this point
# can thus be considered precursors. We dont need to store them all again, they
# have been evaluated to this point: All future jobs can be added to each of them,
# so we only need to store the most profitable one. We want to effeciently compare
# chains, so we can store them in a heap (sorted by END time), pop all non-conflicting
# chains (those that end before the current jobs start) into a list, add the current job
# to the most profitable of the list and push only that onto the heap. The rest of the
# heap are the jobs that are "still cooking".
# NOTE: If there are NO chains that we can add to (the popped list is empty)
# Then we start a new chain using this job as the first link

# For the heap what do we need to store? Ending time and curr_profit for that chain
# I think right? Stored as a list with ending time first, that makes heapifying it
# simple

import heapq


class Solution:
    def jobScheduling(
        self, startTime: List[int], endTime: List[int], profit: List[int]
    ) -> int:
        jobs = sorted(
            [(startTime[i], endTime[i], profit[i]) for i in range(len(startTime))]
        )
        # there is not "heap", DS. Rather, you use heap ops to maintain a list AS a heap
        # Instantiate first job
        job_chains = []
        # heapq.heappush(job_chains, (jobs[0][1],jobs[0][2]))

        # retains the "best" previous profit chain so we can add new jobs to it
        # without having to restore all previous chains to the heap. Its sort of a DP
        # type algorithm: We've already calculated the best profit for all job combinations
        # prior to this new one we are looking at.
        max_profit = 0
        for i in range(len(jobs)):
            extendable_chains = []
            while job_chains and job_chains[0][0] <= jobs[i][0]:
                popped_chain = heapq.heappop(job_chains)
                max_profit = max(max_profit, popped_chain[1])
                extendable_chains.append(popped_chain)
            if not extendable_chains:
                # not so sure about adding max_profit here, but shouold work?
                heapq.heappush(job_chains, (jobs[i][1], jobs[i][2] + max_profit))
            else:
                # heapq.heappush(job_chains, (jobs[i][1],max(extendable_chains, key=lambda x:x[1])[1] + jobs[i][2]))
                heapq.heappush(job_chains, (jobs[i][1], max_profit + jobs[i][2]))
        return max([x[1] for x in job_chains])

        # I think Im getting good at least at coding, if not generating the algorithms in
        # the first place, if "clever, compact,  yet incomprehensible" is a measure of "good"