你打算利用空闲时间来做兼职工作赚些零花钱。
这里有 n
份兼职工作,每份工作预计从 startTime[i]
开始到 endTime[i]
结束,报酬为 profit[i]
。
给你一份兼职工作表,包含开始时间 startTime
,结束时间 endTime
和预计报酬 profit
三个数组,请你计算并返回可以获得的最大报酬。
注意,时间上出现重叠的 2 份工作不能同时进行。
如果你选择的工作在时间 X
结束,那么你可以立刻进行在时间 X
开始的下一份工作。
示例 1:
![](https://assets.leetcode-cn.com/aliyun-lc- upload/uploads/2019/10/19/sample1_1584.png)
**输入:** startTime = [1,2,3,3], endTime = [3,4,5,6], profit = [50,10,40,70]
**输出:** 120
**解释:** 我们选出第 1 份和第 4 份工作,
时间范围是 [1-3]+[3-6],共获得报酬 120 = 50 + 70。
示例 2:
![](https://assets.leetcode-cn.com/aliyun-lc- upload/uploads/2019/10/19/sample22_1584.png)
**输入:** startTime = [1,2,3,4,6], endTime = [3,5,10,6,9], profit = [20,20,100,70,60]
**输出:** 150
**解释:** 我们选择第 1,4,5 份工作。
共获得报酬 150 = 20 + 70 + 60。
示例 3:
![](https://assets.leetcode-cn.com/aliyun-lc- upload/uploads/2019/10/19/sample3_1584.png)
**输入:** startTime = [1,1,1], endTime = [2,3,4], profit = [5,6,4]
**输出:** 6
提示:
1 <= startTime.length == endTime.length == profit.length <= 5 * 10^4
1 <= startTime[i] < endTime[i] <= 10^9
1 <= profit[i] <= 10^4
方法一:动态规划 + 二分查找 由题意知,兼职工作由开始时间 startTime,结束时间 endTime 和预计报酬 profit 三种属性表示。我们首先将兼职工作按结束时间 endTime 从小到大进行排序。使用 dp}[i] 表示前 i 份兼职工作可以获得的最大报酬,即区间 [0, i - 1] 的所有兼职工作可以获得的最大报酬。初始时 dp}[0] = 0,表示没有兼职工作时报酬为 0。那么对于 i \gt 0,根据第 i - 1 份兼职工作是否被选择,我们有以下转移方程:
dp}[i] = \max(\textit{dp}[i - 1], \textit{dp}[k] + \textit{profit}[i - 1])
其中 k 表示满足结束时间小于等于第 i - 1 份工作开始时间的兼职工作数量(因为兼职工作是按照结束时间从小到大进行排序的,所以选择第 i-1 份兼职工作后,我们只能继续选择前 k 份兼职工作),可以通过二分查找获得。
[sol1-Python3] 1 2 3 4 5 6 7 8 9 class Solution : def jobScheduling (self, startTime: List [int ], endTime: List [int ], profit: List [int ] ) -> int : n = len (startTime) jobs = sorted (zip (startTime, endTime, profit), key=lambda p: p[1 ]) dp = [0 ] * (n + 1 ) for i in range (1 , n + 1 ): k = bisect_right(jobs, jobs[i - 1 ][0 ], hi=i, key=lambda p: p[1 ]) dp[i] = max (dp[i - 1 ], dp[k] + jobs[i - 1 ][2 ]) return dp[n]
[sol1-C++] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Solution {public : int jobScheduling (vector<int > &startTime, vector<int > &endTime, vector<int > &profit) { int n = startTime.size (); vector<vector<int >> jobs (n); for (int i = 0 ; i < n; i++) { jobs[i] = {startTime[i], endTime[i], profit[i]}; } sort (jobs.begin (), jobs.end (), [](const vector<int > &job1, const vector<int > &job2) -> bool { return job1[1 ] < job2[1 ]; }); vector<int > dp (n + 1 ) ; for (int i = 1 ; i <= n; i++) { int k = upper_bound (jobs.begin (), jobs.begin () + i - 1 , jobs[i - 1 ][0 ], [&](int st, const vector<int > &job) -> bool { return st < job[1 ]; }) - jobs.begin (); dp[i] = max (dp[i - 1 ], dp[k] + jobs[i - 1 ][2 ]); } return dp[n]; } };
[sol1-Java] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class Solution { public int jobScheduling (int [] startTime, int [] endTime, int [] profit) { int n = startTime.length; int [][] jobs = new int [n][]; for (int i = 0 ; i < n; i++) { jobs[i] = new int []{startTime[i], endTime[i], profit[i]}; } Arrays.sort(jobs, (a, b) -> a[1 ] - b[1 ]); int [] dp = new int [n + 1 ]; for (int i = 1 ; i <= n; i++) { int k = binarySearch(jobs, i - 1 , jobs[i - 1 ][0 ]); dp[i] = Math.max(dp[i - 1 ], dp[k] + jobs[i - 1 ][2 ]); } return dp[n]; } public int binarySearch (int [][] jobs, int right, int target) { int left = 0 ; while (left < right) { int mid = left + (right - left) / 2 ; if (jobs[mid][1 ] > target) { right = mid; } else { left = mid + 1 ; } } return left; } }
[sol1-C#] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public class Solution { public int JobScheduling (int [] startTime, int [] endTime, int [] profit ) { int n = startTime.Length; int [][] jobs = new int [n][]; for (int i = 0 ; i < n; i++) { jobs[i] = new int []{startTime[i], endTime[i], profit[i]}; } Array.Sort(jobs, (a, b) => a[1 ] - b[1 ]); int [] dp = new int [n + 1 ]; for (int i = 1 ; i <= n; i++) { int k = BinarySearch(jobs, i - 1 , jobs[i - 1 ][0 ]); dp[i] = Math.Max(dp[i - 1 ], dp[k] + jobs[i - 1 ][2 ]); } return dp[n]; } public int BinarySearch (int [][] jobs, int right, int target ) { int left = 0 ; while (left < right) { int mid = left + (right - left) / 2 ; if (jobs[mid][1 ] > target) { right = mid; } else { left = mid + 1 ; } } return left; } }
[sol1-C] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #define MAX(a, b) ((a) > (b) ? (a) : (b)) int binarySearch (const int jobs[][3 ], int right, int target) { int left = 0 ; while (left < right) { int mid = left + (right - left) / 2 ; if (jobs[mid][1 ] > target) { right = mid; } else { left = mid + 1 ; } } return left; } static inline int cmp (const void *pa, const void *pb) { int *a = (int *)pa; int *b = (int *)pb; return a[1 ] - b[1 ]; } int jobScheduling (int * startTime, int startTimeSize, int * endTime, int endTimeSize, int * profit, int profitSize) { int n = startTimeSize; int jobs[n][3 ]; for (int i = 0 ; i < n; i++) { jobs[i][0 ] = startTime[i]; jobs[i][1 ] = endTime[i]; jobs[i][2 ] = profit[i]; } qsort(jobs, n, sizeof (jobs[0 ]), cmp); int dp[n + 1 ]; memset (dp, 0 , sizeof (dp)); for (int i = 1 ; i <= n; i++) { int k = binarySearch(jobs, i - 1 , jobs[i - 1 ][0 ]); dp[i] = MAX(dp[i - 1 ], dp[k] + jobs[i - 1 ][2 ]); } return dp[n]; }
[sol1-JavaScript] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 var jobScheduling = function (startTime, endTime, profit ) { const n = startTime.length ; const jobs = new Array (n).fill (0 ).map ((_, i ) => [startTime[i], endTime[i], profit[i]]); jobs.sort ((a, b ) => a[1 ] - b[1 ]); const dp = new Array (n + 1 ).fill (0 ); for (let i = 1 ; i <= n; i++) { const k = binarySearch (jobs, i - 1 , jobs[i - 1 ][0 ]); dp[i] = Math .max (dp[i - 1 ], dp[k] + jobs[i - 1 ][2 ]); } return dp[n]; } const binarySearch = (jobs, right, target ) => { let left = 0 ; while (left < right) { const mid = left + Math .floor ((right - left) / 2 ); if (jobs[mid][1 ] > target) { right = mid; } else { left = mid + 1 ; } } return left; };
[sol1-Golang] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 func jobScheduling (startTime, endTime, profit []int ) int { n := len (startTime) jobs := make ([][3 ]int , n) for i := 0 ; i < n; i++ { jobs[i] = [3 ]int {startTime[i], endTime[i], profit[i]} } sort.Slice(jobs, func (i, j int ) bool { return jobs[i][1 ] < jobs[j][1 ] }) dp := make ([]int , n+1 ) for i := 1 ; i <= n; i++ { k := sort.Search(i, func (j int ) bool { return jobs[j][1 ] > jobs[i-1 ][0 ] }) dp[i] = max(dp[i-1 ], dp[k]+jobs[i-1 ][2 ]) } return dp[n] } func max (a, b int ) int { if b > a { return b } return a }
复杂度分析