目录

裴先生
裴先生
发布于 2022-04-20 / 2 阅读
0
0

算法题:流水线

原创

题目描述

一个工厂有 m 条流水线,来并行完成 n 个独立的作业。该工厂设置了一个调度系统,在安排作业时,总是优先执行处理时间最短的作业。

现给定流水线个数 m,需要完成的作业数 n,每个作业的处理时间分别为 t_1, t_2, \dots, t_n。请你编程计算处理完所有作业的耗时为多少?

调度规则:
n > m 时,首先处理时间短的 m 个作业进入流水线,其他的等待。当某个作业完成时(即某条流水线空闲),依次从剩余作业中取处理时间最短的进入处理。

输入描述

  • 第一行为 2 个整数(采用空格分隔),分别表示流水线个数 m 和作业数 n
  • 第二行输入 n 个整数(采用空格分隔),表示每个作业的处理时长 t_1, t_2, \dots, t_n
  • 约束:0 < m, n < 1000 < t_i < 100
  • 注:保证输入都是合法的。

输出描述

  • 输出处理完所有作业的总时长。

示例 1

输入

3 5
8 4 3 2 10

输出

13

说明

  1. 排序:首先将作业按处理时间升序排序:2, 3, 4, 8, 10
  2. 初始分配:有 3 条流水线,先安排时间最短的 3 个作业(2, 3, 4)分别进入 3 条流水线。
    • 流水线 1:耗时 2
    • 流水线 2:耗时 3
    • 流水线 3:耗时 4
  3. 后续调度
    • 时刻 2:流水线 1 最先完成(耗时 2)。剩余作业中最短的是 8。流水线 1 接手作业 8,流水线 1 总耗时变为 2 + 8 = 10
    • 时刻 3:流水线 2 接下来完成(耗时 3)。剩余作业中最短的是 10。流水线 2 接手作业 10,流水线 2 总耗时变为 3 + 10 = 13
    • 时刻 4:流水线 3 完成(耗时 4)。此时所有作业已分配完毕,流水线 3 空闲。
  4. 最终结果:所有作业处理完的时间取决于最慢的那条流水线。
    • 流水线 1 完成时刻:10
    • 流水线 2 完成时刻:13
    • 流水线 3 完成时刻:4
    • 最大值为 13

💡 解题思路与代码优化

原始代码分析
原始代码尝试使用 i % m 来分配任务,这在任务已经排序且按照轮询方式分配时是可行的,但逻辑上不如直接寻找“最早空闲的流水线”直观和稳健。
此外,原始代码在 n <= m 时直接打印结果后没有 return,会导致后续代码继续执行,可能引发数组越界异常。

优化方案

  1. 贪心策略:题目要求“总是优先执行处理时间最短的作业”,因此第一步必须对作业时间数组进行升序排序
  2. 模拟调度
    • 我们需要维护 m 条流水线当前的完成时刻
    • 对于剩余的每一个作业(从第 m+1 个开始),我们应该将其分配给当前完成时刻最早(即数值最小)的那条流水线。
    • 分配后,更新该流水线的完成时刻(原完成时刻 + 新作业时长)。
  3. 数据结构:可以使用一个数组或列表来存储每条流水线的当前完成时间。每次分配时,遍历该数组找到最小值及其索引。
  4. 结果:所有作业分配完毕后,数组中的最大值即为总耗时。

优化后的 Java 代码

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;

/**
 * 题目:流水线调度
 * 优化思路:贪心算法 + 模拟
 */
public class AssembleLineOptimized {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // 读取第一行 m 和 n
        if (!scanner.hasNextLine()) return;
        String[] mnLine = scanner.nextLine().trim().split(" ");
        int m = Integer.parseInt(mnLine);
        int n = Integer.parseInt(mnLine);[[source_group_web_1]]

        // 读取第二行作业时间
        if (!scanner.hasNextLine()) return;
        String[] timeLine = scanner.nextLine().trim().split(" ");
        int[] times = new int[n];
        for (int i = 0; i < n; i++) {
            times[i] = Integer.parseInt(timeLine[i]);
        }

        // 1. 贪心策略:作业时间升序排序
        Arrays.sort(times);

        // 2. 边界情况:如果作业数 <= 流水线数
        // 所有作业可以同时开始,总耗时就是最长的那个作业时间
        if (n <= m) {
            System.out.println(times[n - 1]);
            return; // 重要:直接返回,避免后续逻辑错误
        }

        // 3. 模拟流水线
        // lines[i] 表示第 i 条流水线当前的完成时刻
        List<Integer> lines = new ArrayList<>();
        
        // 初始化:前 m 个作业分别分配给 m 条流水线
        for (int i = 0; i < m; i++) {
            lines.add(times[i]);
        }

        // 处理剩余的作业 (从第 m 个索引开始,即第 m+1 个作业)
        for (int i = m; i < n; i++) {
            int jobTime = times[i];
            
            // 找到当前完成时刻最早的流水线(最小值)
            int minTime = Collections.min(lines);
            int minIndex = lines.indexOf(minTime);
            
            // 将该作业分配给这条流水线
            // 新的完成时刻 = 当前完成时刻 + 新作业耗时
            lines.set(minIndex, minTime + jobTime);
        }

        // 4. 结果是所有流水线完成时刻的最大值
        int maxTime = Collections.max(lines);
        System.out.println(maxTime);
    }
}

原创

版权声明:本博客原创文章,由 裴先生 2022年04月20日 发表。
转载说明:除特殊说明外本站文章皆由 CC BY-NC-SA 4.0 协议发布,转载须注明出处。


评论