讨论Leetcode问题工作时间表最低难度的更好方法 时间复杂度为รยקคгรђשค

问题描述

您要计划d天的作业列表。作业是依赖的(即,要执行第i个作业,您必须完成0

您每天必须至少完成一项任务。工作计划的难度是d天每一天的难度之和。一天的难度是当天完成工作的最大难度。

给出一个整数数组jobDifficulty和一个整数d。第i个工作的难点是jobDifficulty [i]。

返回作业计划的最低难度。如果找不到工作时间表,则返回-1。

Input: jobDifficulty = [6,5,4,3,2,1],d = 2
Output: 7
Explanation: First day you can finish the first 5 jobs,total difficulty = 6.
Second day you can finish the last job,total difficulty = 1.
The difficulty of the schedule = 6 + 1 = 7 

对于上述问题,我有以下解决方案:

from functools import lru_cache
class Solution:
    def minDifficulty(self,jobDifficulty: List[int],d: int) -> int:
        @lru_cache(None)
        def oneDayDifficulty(start,end):
            if start + 1 == end:
                return jobDifficulty[start]
            mid = (start + end) // 2
            return max(oneDayDifficulty(start,mid),oneDayDifficulty(mid,end))       
        @lru_cache(None)
        def dp(nextTask,days):
            if days == 1:
                return oneDayDifficulty(0,nextTask)
            res = float("inf")
            for startTask in range(days - 1,nextTask):
                res = min(res,dp(startTask,days - 1) + oneDayDifficulty(startTask,nextTask))
            return res
        res = dp(len(jobDifficulty),d)
        return -1 if res == float('inf') else res

我不确定时间和空间的复杂度,它将是O(d * n ^ 2)吗? 另外,您对此问题有更好的解决方案吗?

解决方法

我们可以有O(d * n * log n)。令dp[i][d]代表分配给第i天的第d个任务的最佳选择。然后:

dp[i][d] ->
  min (
    max(A[j..i]) + dp[j-1][d-1]
  ) for d - 1 ≤ j ≤ i

我们可以将到目前为止已经看到的所有dp[j][d]存储在由A[j]排序的二进制搜索树中,并添加与左侧子树中任何节点关联的最小dp[j-1][d-1]的修饰A[j] + dp[j-1][d-1]与右子树中的任何节点关联;以及与之配对的j。当我们到达A[i]时,我们在树中查找其位置,并跟踪与高于它的任何节点关联的最小A[j] + dp[j-1][d-1]和与任何节点关联的最小dp[j-1][d-1]或低于它。然后我们的选择变为:

dp[i][d] ->
  min (
    // A[i] is the first value on day d
    A[i] + dp[i-1][d-1],// A[i] would not affect this one
    A[j_higher] + dp[j_higher-1][d-1],// A[i] becomes the max for this one
    A[i] + dp[j_eq_or_lower-1][d-1]
  )
  where j_higher is the one paired with the minimum A[j] + dp[j-1][d-1]
  associated with a higher A[j],and j_eq_or_lower is the one paired
  with the minimum dp[j-1][d-1] associated with a lower or equal A[j]

插入{,更新装饰并在O(log n)中的树中查找,然后我们对每个O(n)执行d次。

,

我们可以利用堆栈使用O(d*N)空间在O(N)时间上进行操作。

说明:

dp公式:

公式与here几乎没有什么不同。由于我们可以使用堆栈来计算当日d迄今发现的最大难度费用。

dp[i][d] -  represent minimum difficulty that can be achieved considering d days and first i jobs.

重复发生:

有3种情况:

  1. ith工作是第d天唯一的工作,其余所有工作在d天之前完成。
  2. 我们尝试添加到当前日期,看看A[i]是否比当前j之前完成的任何更高难度的工作d更具难度
  3. 我们今天j之前看到过一些难度较高的工作d

dp[i][d] = min( A[i] + dp[i-1][d-1],// ith job is the only job on day d 
                A[i] + (dp[j][d] - A[j]),// or we try adding to current day and see if A[i] has more difficulty than any previous higher difficulty job j already completed on present day
                dp[j][d] // or there is some higher difficulty job `j` we saw previously on present day `d`.
              )

C ++实现:

我们维护堆栈以计算到目前为止所见的最高难度作业j。同样,如在循环中看到的那样,我们只需要前一天的值即可计算当日的值,因此我们在那里节省了空间。

int minDifficulty(vector<int>& A,int D) {
    int jobs = A.size();
    if (jobs < D) return INT_MAX ; // impossible to do atleast 1 job everyday
    int max_difficulty = *max_element(A.begin(),A.end()) + 1;
    vector<int> prv_day(jobs,max_difficulty),curr_day(jobs);
    for (int days = 0; days < D; ++days) {
        vector<int> stack;
        for (int i = days; i < jobs; i++) {
            curr_day[i] = i > 0 ? prv_day[i - 1] + A[i] : A[i]; // start fresh day with first job as ith
            while (!stack.empty() && A[stack.back()] <= A[i]) {
                int j = stack.back(); stack.pop_back();
                curr_day[i] = min(curr_day[i],curr_day[j] - A[j] + A[i]); // or we try extending and see if A[i] has more difficulty than any previous job j on present day 
            }
            if (!stack.empty()) {
                curr_day[i] = min(curr_day[i],curr_day[stack.back()]); // or there is some higher difficulty job we saw previously
            }
            stack.push_back(i);
        }
        swap(prv_day,curr_day);
    }
    return prv_day[jobs - 1];
}

时间复杂度:

每天,我们需要O(N)的时间进行计算。因此,时间复杂度为O(d*N)。堆栈中的项目最多被推送和弹出O(N)次。

额外的空间复杂度:

O(N)用于存储当前和先前状态,而O(N)用于存储堆栈。

,

时间复杂度为รยקคгรђשค

时间复杂度为O(d N ^ 2)。有N d个州,并为 每个状态最多为O(N)。

  • 同一件事,实现起来可能有点简单:
class Solution:
    def minDifficulty(self,nums,d):
        @lru_cache(None)
        def depth_first_search(i,d):
            if d == 1:
                return max(nums[i:])
            res,max_days = float('inf'),0
            for j in range(i,len(nums) - d + 1):
                max_days = max(max_days,nums[j])
                res = min(res,max_days + depth_first_search(j + 1,d - 1))
            return res

        return -1 if len(nums) < d else depth_first_search(0,d)