【线性 dp】A009_LC_规划兼职工作(dp / 二分优化)

一、Problem

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, you need to output the maximum profit you can take such that there are no 2 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.
在这里插入图片描述

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.

Constraints:

1 <= startTime.length == endTime.length == profit.length <= 5 * 10^4
1 <= startTime[i] < endTime[i] <= 10^9
1 <= profit[i] <= 10^4

二、Solution

方法一:

  • 定义状态
    • f [ i ] f[i] 表示询问到第 i i 份工作时,得到的最大报酬
  • 思考初始化:
    • f [ 0 ] = j o b s [ 0 ] . p r o f i t f[0] = jobs[0].profit
  • 思考状态转移方程
    • f [ i ] = m a x ( f [ i 1 ] , f [ p r e J o b I D ] + j o b s [ i ] . p r o f i t ) f[i] = max(f[i-1], f[preJobID] + jobs[i].profit) ,对于第 i ( i 1 ) i (i \geqslant 1) 份工作,可以选择不做也可以选择做,做的话就要连着前一份工作
  • 思考输出 f [ n ] f[n]

开始写的是这种,然后很不幸,过不了…

[1,1,1]
[2,3,4]
[5,6,4]
预期:6
输出:11
class Solution {
    public int jobScheduling(int[] st, int[] et, int[] pf) {
        int n = st.length, f[] = new int[n];
        Job[] js = new Job[n];
        for (int i = 0; i < n; i++) js[i] = new Job(st[i], et[i], pf[i]);
        Arrays.sort(js, (e1, e2) -> e1.e - e2.e);

        int pre[] = new int[n];
        for (int i = 1; i < n; i++)
        for (int j = i-1; j >= 0; j--) if (js[j].e <= js[i].s) {
            pre[i] = j;
            break;
        }
        f[0] = js[0].p;
        for (int i = 1; i < n; i++) {
            f[i] = Math.max(f[i-1], f[pre[i]] + js[i].p);
        }
        return f[n-1];
    }
    class Job {
        int s, e, p;
        Job(int s, int e, int p) {this.s = s;this.e = e;this.p = p;}
        Job(){}
    }
}

原因是当某些工作的开始时间相同的话,只能选择其中的一份,而下面下标从 1 开始运算的话,相当于增加了一份报酬为 0 的假工作,使得第一份工作可以进行比较,而如果从 0 开始,那么第一份工作将不可以比较。

class Solution {
    public int jobScheduling(int[] st, int[] et, int[] pf) {
        int n = st.length, f[] = new int[n+1];
        Job[] js = new Job[n+1];
        js[0] = new Job();
        for (int i = 1; i <= n; i++) js[i] = new Job(st[i-1], et[i-1], pf[i-1]);
        Arrays.sort(js, (e1, e2) -> e1.e - e2.e);

        int pre[] = new int[n+1];
        for (int i = 2; i <= n; i++)
        for (int j = i-1; j >= 1; j--) if (js[i].s >= js[j].e) {
            pre[i] = j;
            break;
        }
        f[1] = js[1].p;
        for (int i = 2; i <= n; i++) {
            f[i] = Math.max(f[i-1], f[pre[i]] + js[i].p);
        }
        return f[n];
    }
    class Job {
        int s, e, p;
        Job(int s, int e, int p) {this.s = s;this.e = e;this.p = p;}
        Job(){}
    }
}

复杂度分析

  • 时间复杂度: O ( n 2 ) O(n^2)
  • 空间复杂度: O ( n ) O(n)

方法二:二分优化

我们对 jobs 数组进行了排序,所以我们可以用二分来查找出工作 i i 的前一份可行的工作来优化 O ( n 2 ) O(n^2) 查找前一份工作。


复杂度分析

  • 时间复杂度: O ( ) O()
  • 空间复杂度: O ( ) O()

猜你喜欢

转载自blog.csdn.net/qq_43539599/article/details/106843541