初始想法
这道题第一反应是用 dp 来做。
- 首先是创建一个 class Job,里面有三个属性 start,end,profit。
class Job{
int start;
int end;
int profit;
public Job(int start, int end, int profit) {
this.start = start;
this.end = end;
this.profit = profit;
}
}
- 然后对所有的 Job 按照 end 时间进行排序,这样递推才好进行。
Job[] jobs = new Job[startTime.length];
for (int i = 0; i < jobs.length; i++) {
jobs[i] = new Job(startTime[i], endTime[i], profit[i]);
}
Arrays.sort(jobs, Comparator.comparingInt(o -> o.end));
- 再用一个 dp 数组储存以当前项为末尾时的最大 profit,递推完后再重新遍历一遍 dp 得到的最大值即为结果。
int[] dp = new int[jobs.length];
for (int i = 0; i < jobs.length; i++) {
dp[i]=jobs[i].profit;
}
for (int i = 1; i < jobs.length; i++) {
for (int j = 0; j < i; j++)
if (jobs[i].start >= jobs[j].end) {
dp[i] = Math.max(dp[i], dp[j] + jobs[i].profit);
}
}
int res = 0;
for (int i : dp) {
res = Math.max(res, i);
}
return res;
但提交上去后显示 Time Limit Exceeded 。。。
分析一下复杂度,每次取定
i
i
i 后
j
j
j 都要从 0 遍历到
i
−
1
i - 1
i−1,所以总的复杂度是
O
(
N
2
)
O(N^2)
O(N2)。
优化
有没有办法在遍及 j j j 时不重新从 0 遍历到 i − 1 i-1 i−1 呢?
关键点在于把 dp 数组的意义改变一下。之前 dp 数组储存的是以当前项为末尾时最大 profit。而现在认为 dp[i] 的意义是区间 0~ i i i 里的最大profit,但不一定以 i i i 结尾!
这样一来,对于每一个 i i i , j j j 从 i − 1 i-1 i−1开始遍历,直到 0,中途如果找到与当前 job 时间错开的项 jobs[j],则不再需要向前遍历了。原因是 jobs 数组是以 end 升序排列的, j j j 之前项的最大 dp 值必然小于 dp[j]。
最后的代码如下:
class Solution {
public int jobScheduling(int[] startTime, int[] endTime, int[] profit) {
Job[] jobs = new Job[startTime.length];
for (int i = 0; i < jobs.length; i++) {
jobs[i] = new Job(startTime[i], endTime[i], profit[i]);
}
Arrays.sort(jobs, Comparator.comparingInt(o -> o.end));
int[] dp = new int[jobs.length];
for (int i = 0; i < jobs.length; i++) {
dp[i]=jobs[i].profit;
}
for (int i = 1; i < jobs.length; i++) {
dp[i] = Math.max(dp[i-1], jobs[i].profit);
for (int j = i-1; j >=0; j--)
if (jobs[i].start >= jobs[j].end) {
dp[i] = Math.max(dp[i], dp[j] + jobs[i].profit);
break;
}
}
return dp[jobs.length-1];
}
public static void main(String[] args) {
int[] a = {1,1,1};
int[] b = {2,3,4};
int[] c = {5,6,4};
System.out.println(new Solution().jobScheduling(a,b,c));
}
}
class Job{
int start;
int end;
int profit;
public Job(int start, int end, int profit) {
this.start = start;
this.end = end;
this.profit = profit;
}
}