We have n jobs, where every job is scheduled to be done from startTime[i] to endTime[i], obtaining a profit of profit[i].
You're given the startTime, endTime and profit arrays, return the maximum profit you can take such that there are no two jobs in the subset with overlapping time range.
If you choose a job that ends at time X you will be able to start another job that starts at time X.
Example 1:

Input: startTime = [1,2,3,3], endTime = [3,4,5,6], profit = [50,10,40,70]
Output: 120
Explanation: The subset chosen is the first and fourth job.
Time range [1-3]+[3-6] , we get profit of 120 = 50 + 70.
Example 2:

Input: startTime = [1,2,3,4,6], endTime = [3,5,10,6,9], profit = [20,20,100,70,60]
Output: 150
Explanation: The subset chosen is the first, fourth and fifth job.
Profit obtained 150 = 20 + 70 + 60.
Example 3:

Input: startTime = [1,1,1], endTime = [2,3,4], profit = [5,6,4]
Output: 6
Constraints:
1 <= startTime.length == endTime.length == profit.length <= 5 * 1041 <= startTime[i] < endTime[i] <= 10^91 <= profit[i] <= 10^4
這道題說是給了n個工作,每個工作的起始時間,結束時間,和利潤分別放在對應的三個數組里面,要求每個時間點只能做一份工作,問可以得到的最大利潤是多少。這是一道典型的背包問題 Knapsack Problem,對於此類問題,動態規划 Dynamic Programming 就是不二之選。首先來定義 DP 數組,這里要求的是在某個時間段內的最大利潤,但並沒有給定具體的結束時間,所以這里用一個映射比較合適,同時我們還希望保持時間的順序,則可以用一個 TreeMap 來建立結束工作的時間和所獲得的利潤之間的映射,初始化時將 {0, 0} 的映射對兒加入,后面會解釋原因。由於給定的工作可能是亂序的,這樣不利於知道某個時間段內的所有工作,需要按照結束時間給所有工作排個序,為啥是結束時間呢?因為只有某個工作在給定的時間內結束了,才可以獲得該工作的利潤。題目中將起始時間,結束時間,和利潤放到了三個不同的數組中,這里新建一個二維數組,將同一個工作的三個信息放到一起,將結束時間放到第一個,因為要根據其進行排序。
按結束時間排好序了之后就可以開始遍歷所有工作了,對於遍歷到的工作,就需要采用類似背包問題的更新方法了,若不干這份工作,什么都不用更新,若干了這份工作,那么跟這份工作時間沖突的其他的工作就不能干了,這份工作的起始時間已知,只要在該起始時間之前找到一個最大的利潤值,再加上當前工作的利潤,若整體的利潤值最大,就可以用更新當前工作結束時間的 dp 值。用二分搜索法來進行查找可以提高效率,在 C++ 中使用 upper_bound 來查找第一個大於目標值的數,然后向前退一位,就是第一個小於等於目標值的數了,由於這里需要退位操作,為了防止越界,所以初始化時提前加入了 {0, 0} 的映射對兒。找到之前的最大利潤后,加上當前工作的利潤值,若這個總利潤值大於最后的時間點的 dp 值時,更新當前工作結束時間的 dp 值。因為最終希望整體的利潤最高,所以每次需要跟最后時間點的 dp 值進行比較,參見代碼如下:
class Solution {
public:
int jobScheduling(vector<int>& startTime, vector<int>& endTime, vector<int>& profit) {
vector<vector<int>> jobs;
map<int, int> dp{{0, 0}};
for (int i = 0; i < startTime.size(); ++i) {
jobs.push_back({endTime[i], startTime[i], profit[i]});
}
sort(jobs.begin(), jobs.end());
for (auto &job : jobs) {
int cur = prev(dp.upper_bound(job[1]))->second + job[2];
if (cur > dp.rbegin()->second) dp[job[0]] = cur;
}
return dp.rbegin()->second;
}
};
Github 同步地址:
https://github.com/grandyang/leetcode/issues/1235
類似題目:
Maximum Earnings From Taxi
參考資料:
https://leetcode.com/problems/maximum-profit-in-job-scheduling/
https://leetcode.com/problems/maximum-profit-in-job-scheduling/discuss/409188/C%2B%2B-with-picture
