LC每日一题|20240504 - 1235. 规划兼职工作

76 阅读2分钟

LC每日一题|20240504 - 1235. 规划兼职工作

你打算利用空闲时间来做兼职工作赚些零花钱。

这里有 n 份兼职工作,每份工作预计从 startTime[i] 开始到 endTime[i] 结束,报酬为 profit[i]

给你一份兼职工作表,包含开始时间 startTime,结束时间 endTime 和预计报酬 profit 三个数组,请你计算并返回可以获得的最大报酬。

注意,时间上出现重叠的 2 份工作不能同时进行。

如果你选择的工作在时间 X 结束,那么你可以立刻进行在时间 X 开始的下一份工作。

提示:

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

题目级别: Hard

解题思路

是一道比较简单dp

对于时间轴上的某个时间点来说,其状态转移为其前一个时间点的报酬所有在此时间点结束的任务完成时所取得的报酬值。观察取值范围得知,时间点的范围实在是太大了,直接跑肯定会炸,所以应先处理出特征点,即所有的开始时间与结束时间取并集,然后再对其排序,作为时间轴。

在遍历过程中,确定某个时间点在时间轴上的位置有很多种方案,比如可以直接用二分查找。但是由于二分对我来说是妥妥的Hard,所以我采用了更菜鸡但是效率更高的hashMap,映射每个特征时间点到时间轴的坐标。

AC代码

class Solution {
    fun jobScheduling(startTime: IntArray, endTime: IntArray, profit: IntArray): Int {
        val set = HashSet<Int>()
        startTime.forEach { set.add(it) }
        endTime.forEach { set.add(it) }
        val time = set.toMutableList().apply { sort() }
        val table = HashMap<Int, Int>()
        for (i in time.indices) table[time[i]] = i
        val dp = IntArray(time.size)
        val work = HashMap<Int, ArrayList<IntArray>>()
        for (i in startTime.indices) {
            work[startTime[i]] = (work[startTime[i]] ?: arrayListOf()).apply {
                add(intArrayOf(endTime[i], profit[i]))
            }
        }
        for (i in dp.indices) {
            dp[i] = Math.max(dp[i], dp.getOrNull(i - 1) ?: 0)
            work[time[i]]?.forEach {
                val target = table[it[0]]!!
                dp[target] = Math.max(dp[target], dp[i] + it[1])
            }
        }
        return dp.last()
    }
}

时间复杂度:O(nlogn),需要对所有时间点排序并遍历常数次。

空间复杂度:O(n),需要常数倍n的中间量。