leetcode
leetcode 2451 ~ 2500
最小处理时间

最小处理时间

难度:

标签:

题目描述

You have a certain number of processors, each having 4 cores. The number of tasks to be executed is four times the number of processors. Each task must be assigned to a unique core, and each core can only be used once.

You are given an array processorTime representing the time each processor becomes available and an array tasks representing how long each task takes to complete. Return the minimum time needed to complete all tasks.

 

Example 1:

Input: processorTime = [8,10], tasks = [2,2,3,1,8,7,4,5]

Output: 16

Explanation:

Assign the tasks at indices 4, 5, 6, 7 to the first processor which becomes available at time = 8, and the tasks at indices 0, 1, 2, 3 to the second processor which becomes available at time = 10

The time taken by the first processor to finish the execution of all tasks is max(8 + 8, 8 + 7, 8 + 4, 8 + 5) = 16.

The time taken by the second processor to finish the execution of all tasks is max(10 + 2, 10 + 2, 10 + 3, 10 + 1) = 13.

Example 2:

Input: processorTime = [10,20], tasks = [2,3,1,2,5,8,4,3]

Output: 23

Explanation:

Assign the tasks at indices 1, 4, 5, 6 to the first processor and the others to the second processor.

The time taken by the first processor to finish the execution of all tasks is max(10 + 3, 10 + 5, 10 + 8, 10 + 4) = 18.

The time taken by the second processor to finish the execution of all tasks is max(20 + 2, 20 + 1, 20 + 2, 20 + 3) = 23.

 

Constraints:

  • 1 <= n == processorTime.length <= 25000
  • 1 <= tasks.length <= 105
  • 0 <= processorTime[i] <= 109
  • 1 <= tasks[i] <= 109
  • tasks.length == 4 * n

代码结果

运行时间: 92 ms, 内存: 31.0 MB


import java.util.Arrays;
import java.util.PriorityQueue;
import java.util.stream.IntStream;

// 思路:
// 1. 将任务数组按任务时间从大到小排序。
// 2. 为每个处理器建立一个最小堆,用于记录其每个核心的空闲时间。
// 3. 逐个将任务分配给最早空闲的核心。
// 4. 返回所有处理器中完成所有任务的最晚时间。

public class Solution {
    public int minTime(int[] processorTime, int[] tasks) {
        // 将任务数组按任务时间从大到小排序
        Arrays.sort(tasks);
        int n = processorTime.length;
        int cores = 4; // 每个处理器4个核心

        // 最小堆,用于记录每个核心的空闲时间
        PriorityQueue<Integer>[] processors = IntStream.range(0, n)
                .mapToObj(i -> new PriorityQueue<Integer>())
                .toArray(PriorityQueue[]::new);

        IntStream.range(0, n).forEach(i -> IntStream.range(0, cores)
                .forEach(j -> processors[i].offer(processorTime[i])));

        // 任务从大到小分配
        IntStream.iterate(tasks.length - 1, i -> i >= 0, i -> i - 1).forEach(i -> {
            // 寻找最早空闲的核心
            int minIndex = IntStream.range(0, n)
                    .reduce((a, b) -> processors[a].peek() < processors[b].peek() ? a : b)
                    .getAsInt();
            // 取出最早空闲的核心时间并更新
            int earliest = processors[minIndex].poll();
            processors[minIndex].offer(earliest + tasks[i]);
        });

        // 找到所有处理器中最晚的完成时间
        return IntStream.range(0, n)
                .flatMap(i -> processors[i].stream().mapToInt(Integer::intValue))
                .max()
                .orElse(0);
    }
}

解释

方法:

此题的核心思想是贪心算法。通过将处理器的空闲时间从小到大排序,并将任务所需时间从大到小排序,可以实现最优的任务调度。排序后,将最长的任务分配给最早空闲的处理器,然后每个处理器分配到的第一个任务是它能接受的最大任务。代码中利用zip函数组合处理器和任务数组,通过每四个任务选择一个,确保每个处理器在同一时间只处理一个任务。最后,通过计算每个处理器完成其任务的时间,并取最大值,就能得到所有任务完成所需的最小时间。

时间复杂度:

O(n log n)

空间复杂度:

O(log n)

代码细节讲解

🦆
为什么要将`processorTime`数组进行升序排序,而将`tasks`数组进行降序排序?
通过将`processorTime`数组升序排序,可以确保我们首先分配任务给最早空闲的处理器,从而尽快开始任务的处理。而将`tasks`数组进行降序排序,则可以确保最耗时的任务尽可能早地得到处理,这有助于减少等待时间,使得处理器在处理完其当前任务后,可以立即接手下一项较大的任务,最终实现整体任务完成时间的最小化。这种方法是典型的贪心策略,通过局部最优的选择(最早空闲处理器与最长任务的匹配)来达到全局最优。
🦆
在使用zip函数组合处理器和任务时,为什么选择每四个任务取一个,即使用`tasks[::4]`?这样的取法是否会导致某些处理器任务过多而其他处理器任务不足?
此处选择每四个任务取一个,是基于每个处理器拥有四个核心,每核心执行一个任务的假设。通过选择`tasks[::4]`是为了确保每个处理器在同一时间只接受一个任务。错误理解了题意,题目实际上要求每个核心处理一个任务,而代码中的方法将每四个任务中的最大任务分配给一个处理器,这会导致每个处理器只处理一个任务而非四个,因此会造成资源的浪费。正确的方法应该是每个处理器分配四个任务,而不是通过`tasks[::4]`这种方式。
🦆
本题解中如何确保每个核心只执行一个任务,而不是单个处理器承担多个任务?
题解中的代码实际上没有正确处理每个核心只执行一个任务的要求。正确的方式应该是确保每个处理器的每个核心分别分配到一个任务。题解中的方法误用了`tasks[::4]`,导致每个处理器只分配到每四个任务中的最大一个,没有充分利用处理器的四个核心。正确的做法应该是,对于每个处理器,应从任务列表中分配四个任务,每个核心执行一个任务。这需要重新设计任务分配策略,以确保每个核心的独立工作,而不仅仅是每个处理器。

相关问题