leetcode
leetcode 1551 ~ 1600
完成所有工作的最短时间

完成所有工作的最短时间

难度:

标签:

题目描述

代码结果

运行时间: 36 ms, 内存: 16.0 MB


/*
思路:
利用Java Stream API,优化代码的可读性。
首先获取作业数组的最大值和总和。
然后在二分查找过程中,使用stream来获取workers的工作时间和。判断能否在给定的最大工作时间下完成任务。
*/

import java.util.Arrays;

public class Solution {
    public int minimumTimeRequired(int[] jobs, int k) {
        int left = Arrays.stream(jobs).max().orElse(0);
        int right = Arrays.stream(jobs).sum();
        while (left < right) {
            int mid = (left + right) / 2;
            if (canFinish(jobs, k, mid)) {
                right = mid;
            } else {
                left = mid + 1;
            }
        }
        return left;
    }

    private boolean canFinish(int[] jobs, int k, int maxTime) {
        int[] workers = new int[k];
        return canDistribute(jobs, workers, 0, maxTime);
    }

    private boolean canDistribute(int[] jobs, int[] workers, int index, int maxTime) {
        if (index == jobs.length) return true;
        for (int i = 0; i < workers.length; i++) {
            if (workers[i] + jobs[index] <= maxTime) {
                workers[i] += jobs[index];
                if (canDistribute(jobs, workers, index + 1, maxTime)) return true;
                workers[i] -= jobs[index];
            }
            if (workers[i] == 0) break; // 剪枝
        }
        return false;
    }
}

解释

方法:

该题解采用二分搜索与回溯的方法来解决问题。首先对工作时间数组进行排序,以便优先尝试分配耗时较长的工作,这有助于尽早达到最大工作时间的限制。在二分搜索的每一步,使用中值 'mid' 作为尝试的最大工作时间限制。在回溯部分,使用一个辅助函数 'check' 来递归尝试将每项工作分配给工人,直到所有工作都能在 'mid' 时间内被成功分配,或者无法分配为止。如果可以成功分配,则缩小搜索范围,否则增大 'mid'。通过这种方式逐步缩小可能的最大工作时间,直到找到最小可能值。

时间复杂度:

O((log(sum(jobs) - max(jobs))) * k^n)

空间复杂度:

O(n + k)

代码细节讲解

🦆
二分搜索的上界和下界分别是`sum(jobs)`和`max(jobs)`的最大值除以工人数`k`,这样设定的理由是什么?
在这个问题中,下界`l`被设为`max(jobs)`,意味着无论工人数如何,单个工人至少需要能够完成最耗时的那项工作。这是因为每项工作必须完整地由一个工人来完成,无法分割。上界`r`被设为`sum(jobs)`,这代表了所有工作如果由一名工人完成所需要的总时间,是可能的最大工作时间。这个上界表示最坏情况,即所有工作由一个人完成的情况。这种设定帮助确保了二分搜索的范围覆盖所有可能的最优解。
🦆
二分搜索的终止条件是当`l < r`,但为什么在找到可行的`mid`后,将`r`设置为`mid`而不是`mid-1`?
在二分搜索中,将`r`设置为`mid`而不是`mid-1`是因为我们正在寻找最小可能的`mid`使得所有工作能够在此时间限制内完成。如果我们设置`r = mid - 1`,我们可能会错过这个最小的`mid`。通过设置`r = mid`,我们确保不会跳过这个界限,而是继续缩小搜索区间直到找到确切的最小界限。这种方法保证了`mid`始终是一个可能的解决方案,而不会因为过早缩小范围而错过正确答案。

相关问题