两机器流水线调度问题(Johnson算法)

两机器流水线调度问题(Johnson算法)

流水线调度问题(Flow Shop Scheduling Problem)是组合优化和算法设计中的一个经典问题,常见于制造业、计算机任务调度等场景。其核心目标是安排多个作业(jobs)在多台机器(machines)上的处理顺序,以最小化总完成时间(makespan)或其他目标函数。在算法竞赛中,这类问题通常需要结合贪心策略或动态规划来解决。


问题模型

输入:有 n 个作业和 m 台机器,每个作业必须依次经过所有机器(顺序固定,如机器1 → 机器2 → … → 机器m)。
处理时间:作业 i 在机器 j 上的处理时间为 p[i][j]
目标:找到作业的调度顺序,使得最后一个作业在所有机器上的完成时间最小(即最小化 makespan)。


经典贪心策略:Johnson算法(两机器流水线调度)

m=2(两台机器)时,Johnson算法 可以找到最优解,其核心是通过贪心排序策略减少机器间的空闲等待时间。

算法步骤
  1. 分类作业
    • 将作业分为两类:
    A类:在机器1的处理时间 ≤ 机器2的处理时间(即 p[i][1] ≤ p[i][2])。
    B类:在机器1的处理时间 > 机器2的处理时间(即 p[i][1] > p[i][2])。
  2. 排序规则
    A类作业按机器1的处理时间升序排序(先处理耗时短的)。
    B类作业按机器2的处理时间降序排序(先处理耗时长的)。
  3. 合并顺序:按 A类作业排序结果 + B类作业排序结果 的顺序调度所有作业。
正确性原理

A类作业优先处理机器1耗时短的,让机器2尽快开始工作。
B类作业优先处理机器2耗时长的,避免机器2长时间空闲。

示例

假设有4个作业,在两台机器上的处理时间如下:

作业机器1时间机器2时间
136
252
314
467

A类作业p[i][1] ≤ p[i][2]):作业1、3、4。
• 按机器1时间升序排序:作业3(1)→ 作业1(3)→ 作业4(6)。
B类作业p[i][1] > p[i][2]):作业2。
• 按机器2时间降序排序:作业2(2)。
最终顺序:3 → 1 → 4 → 2。总完成时间为18。


多机器流水线调度(m > 2)

当机器数超过2时,问题变为NP-hard,无法在多项式时间内找到精确解。常用方法包括:

  1. 贪心启发式
    NEH算法(Nawaz-Enscore-Ham):按总处理时间降序插入作业,每次选择最优插入位置。
  2. 动态规划:适用于小规模问题(如n ≤ 20)。
  3. 元启发式算法:遗传算法、模拟退火等(竞赛中较少用)。

算法竞赛中的常见变种

  1. 带准备时间的流水线调度
    • 机器切换作业时需要准备时间,需调整排序策略。
  2. 抢占式调度
    • 允许中断当前作业以处理更高优先级的任务(需用优先队列)。
  3. 并行机器
    • 每个阶段有多台并行机器(如LeetCode的“任务调度器”问题)。

典型例题

  1. 两机器流水线调度(Johnson算法直接应用):
    • 题目描述:给定n个作业在两台机器的时间,求最小makespan。
    • 代码实现:排序后模拟调度过程。
  2. HDOJ 1078(类似流水线调度):需结合贪心策略优化作业顺序。
  3. LeetCode 621(任务调度器):虽然不是严格流水线问题,但涉及类似的贪心策略(空闲时间填充)。

例题:蓝桥算法赛破译密码
在这里插入图片描述

代码:
c++:

#include <iostream>
#include <algorithm>
using namespace std;

pair<int, int> a[1005];

int main() {
  int n;
  cin>>n;
  for (int i=1; i<=n; i++)
    cin>>a[i].first;
  for (int i=1; i<=n; i++)
    cin>>a[i].second;
  sort(a+1, a+1+n, [](pair<int, int> &p1, pair<int, int> &p2) {
    if (p1.first <= p1.second && p2.first > p2.second)
      return true;
    if (p2.first <= p2.second && p1.first > p1.second)
      return false;
    if (p1.first <= p1.second)
      return p1.first < p2.first;
    else
      return p1.second > p2.second;
  });
  int ans = 0, t = 0;
  for (int i=1; i<=n; i++)
    t += a[i].first, ans = max(ans, t) + a[i].second;
  cout<<ans<<'\n';
  
  return 0;
}

java:

import java.util.*;

// 自定义包含两个整数的类
class PII {
    public int first;
    public int second;
    
    public PII(int first, int second) {
        this.first = first;
        this.second = second;
    }
}

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        PII[] jobs = new PII[n];
        
        // 读取机器1的处理时间
        for (int i = 0; i < n; i++) {
            jobs[i] = new PII(scanner.nextInt(), 0);
        }
        // 读取机器2的处理时间并填充对象
        for (int i = 0; i < n; i++) {
            jobs[i].second = scanner.nextInt();
        }
        
        // Johnson算法排序
        Arrays.sort(jobs, new Comparator<PII>() {
            @Override
            public int compare(PII p1, PII p2) {
                boolean p1IsA = (p1.first <= p1.second);
                boolean p2IsA = (p2.first <= p2.second);
                
                // A类作业排在前,B类在后
                if (p1IsA && !p2IsA) return -1;
                if (!p1IsA && p2IsA) return 1;
                
                // 同类作业的排序规则
                if (p1IsA) {
                    return Integer.compare(p1.first, p2.first); // A类升序
                } else {
                    return Integer.compare(p2.second, p1.second); // B类降序
                }
            }
        });
        
        // 计算总完成时间
        int timeMachine1 = 0;
        int makespan = 0;
        for (PII job : jobs) {
            timeMachine1 += job.first;
            makespan = Math.max(makespan, timeMachine1) + job.second;
        }
        
        System.out.println(makespan);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值