[NOIP 2006 提高组] 作业调度方案

GitHub跳转原题关系图返回列表

按给定顺序逐个安排操作,在工件前序完成后寻找目标机器最早连续空闲时间段。

OJ: luogu

题目 ID: P1065

难度:普及/提高-

标签:模拟调度python

日期: 2026-06-19 02:34

题意

m 台机器和 n 个工件。每个工件有 m 道工序,每道工序指定机器和加工时间。题目给出安排顺序,要求每次把当前工序尽量早地插入目标机器的空闲时间段,同时满足同一工件前序工序已完成。

思路

用三个数组维护状态:

  • next_operation[job]:某个工件下一道要安排的工序编号;
  • job_finish_time[job]:某个工件上一道工序完成时间;
  • busy[machine][time]:某台机器某个时刻是否被占用。

处理安排顺序中的一个工件 job 时:

  1. 找到它当前工序的目标机器和加工时长;
  2. 开始时间不能早于 job_finish_time[job]
  3. 从这个时间开始向后找,直到找到一段长度为 duration 的连续空闲时间;
  4. 标记这段机器时间为占用;
  5. 更新工件完成时间和答案。

这正好模拟了题目要求的“尽量靠前插入”。

Python 知识

  • /home/rainboy/mycode/hugo-blog/content/program_language/python/oj_input_output_cheatsheet.md:多行整数矩阵可以逐行 input().split()
  • /home/rainboy/mycode/hugo-blog/content/program_language/python/brute_force_validation.md:二维列表要逐行创建,避免浅拷贝。
  • 布尔矩阵 busy[machine][time] 适合表示时间轴占用。
  • 遇到冲突时把 start 推到冲突时间之后,可以减少无效检查。

代码

cpp
#include <bits/stdc++.h>
using namespace std;

const int MAXM = 25;     // 最大机器数
const int MAXN = 25;     // 最大工件数
const int MAXT = 10005;  // 时间轴上限

int m, n;
int order_list[MAXM * MAXN];        // 安排顺序:按顺序列出每次要安排的工件编号
int machine_id[MAXN][MAXM];         // machine_id[job][step] 工件 job 的第 step 道工序在哪台机器
int cost_time[MAXN][MAXM];          // cost_time[job][step] 工件 job 的第 step 道工序加工时长

int next_step[MAXN];                // 工件 job 下一次要安排第几道工序
int finish_time[MAXN];              // 工件 job 已安排完的最后时间(最早可开始下一道工序的时间)
bool busy[MAXM][MAXT];              // busy[machine][time] 标记该机器该时刻是否被占用

/* ===== 输入层:读取全部原始数据 ===== */
void read_input() {
    cin >> m >> n;
    int tot = m * n;

    for (int i = 1; i <= tot; i++)
        cin >> order_list[i];

    for (int job = 1; job <= n; job++)
        for (int step = 1; step <= m; step++)
            cin >> machine_id[job][step];

    for (int job = 1; job <= n; job++)
        for (int step = 1; step <= m; step++)
            cin >> cost_time[job][step];
}

/* ===== 调度层:核心调度逻辑 ===== */

// 在机器 mac 上从 start 时刻向后找一段长度为 len 的连续空闲时间。
int find_available_slot(int mac, int start, int len) {
    while (true) {
        bool ok = true;
        for (int t = start; t < start + len; t++) {
            if (busy[mac][t]) {
                start = t + 1;     // 遇到冲突,跳到冲突时刻之后继续找
                ok = false;
                break;
            }
        }
        if (ok) break;
    }
    return start;
}

int schedule_all() {
    int tot = m * n;
    int answer = 0;

    for (int i = 1; i <= tot; i++) {
        int job = order_list[i];           // 当前要安排的工件
        int step = ++next_step[job];       // 当前安排的是该工件的第几道工序
        int mac = machine_id[job][step];   // 目标机器
        int len = cost_time[job][step];    // 加工时长

        // 开始时间不能早于该工件上一道工序完成的时间
        int start = finish_time[job];

        // 在目标机器上找到最早的一段连续空闲时间段
        start = find_available_slot(mac, start, len);

        // 占用该机器上的 [start, start+len) 时间段
        for (int t = start; t < start + len; t++)
            busy[mac][t] = true;

        finish_time[job] = start + len;
        if (answer < finish_time[job])
            answer = finish_time[job];
    }
    return answer;
}

/* ===== 主控层:组装各层 ===== */
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    read_input();

    int ans = schedule_all();

    cout << ans << '\n';
    return 0;
}
python
machine_count, job_count = map(int, input().split())
order = [x - 1 for x in map(int, input().split())]
machine_for = [list(map(lambda x: int(x) - 1, input().split())) for _ in range(job_count)]
duration_for = [list(map(int, input().split())) for _ in range(job_count)]

max_time = job_count * machine_count * 20 + 1
busy = [[False for _ in range(max_time)] for _ in range(machine_count)]
next_operation = [0 for _ in range(job_count)]
job_finish_time = [0 for _ in range(job_count)]
answer = 0

for job in order:
    operation = next_operation[job]
    machine = machine_for[job][operation]
    duration = duration_for[job][operation]

    start = job_finish_time[job]
    while True:
        can_place = True
        for time in range(start, start + duration):
            if busy[machine][time]:
                start = time + 1
                can_place = False
                break
        if can_place:
            break

    for time in range(start, start + duration):
        busy[machine][time] = True

    finish = start + duration
    job_finish_time[job] = finish
    next_operation[job] += 1
    answer = max(answer, finish)

print(answer)

复杂度

设最终时间轴长度为 T,共有 n*m 个操作。直接时间轴模拟复杂度约为 O(nmT)O(nmT),本题数据范围很小,可以通过。空间复杂度是 O(mT)O(mT)

总结

调度题不要急着找复杂算法。题目已经规定唯一的靠前插入规则,按顺序找目标机器最早可行空档即可。