洛谷题单指南-模拟和高精度-P1065 [NOIP2006 提高组] 作业调度方案

原题链接:https://www.luogu.com.cn/problem/P1065

题意解读:此题也是典型的模拟法,稍微带一点贪心的思路,题目已经明确了三个最关键的约束条件:

1、对同一个工件,每道工序必须在它前面的工序完成后才能开始;

2、同一时刻每一台机器至多只能加工一个工件;

3、在给机器插入任务的时候,尽量靠前,插入最前面的空挡。

因此,题目的核心就是按照给定的工序顺序,将工序插入到机器的时间表中,比如机器1、时间2已安排操作:machine[1][2] = true

解题思路:

此题的核心算法就是根据工序顺序,进行填表,关键在于几个数据结构的定义,以及填表算法的设计

1、工序信息

根据题意,工序信息包括工序所使用的机器、工序运行的时间,可以通过结构体数组来定义:

struct Task
{
    int machine_no; //工序使用的机器号
    int times; //工序运行时长
};
Task task[N][M]; //工序信息表

2、机器时间表

机器时间表表示每台机器每一个时刻是否被占用,用一个bool数组来定义:

bool machine[M][T]; //机器时间表

这里T表示时间的最大值,可以按此方式估算:最大工件数*最大工序数*最长时间=20*20*20=8000,可以设定为10000。

3、填表算法

第一步:读入工序安排表

工序安排表用结构体数据表示,除了工件号,也要记录工序号

struct Node
{
    int work_no; //工件号
    int seq_no; //工序号
};
Node order[M * N]; //给定的安排顺序

工序号的计算可以借助一个桶数组seq[],如果在读取顺序中遇到工件i,则工序是seq[i]++。

第二步:遍历工序安排表,填入机器时间表

对于一个工序,找到其对应的运行机器,另外还需要找到其可以开始运行的时间。

如何找到其可以运行的时间呢?可以保存每一个工件最后一次运行的时间,则一个工序的起始时间至少要从该工件最后一次运行的时间+1开始。

确定了机器、可以开始运行的时间,就在机器时间表machine[][]中找到一段可以运行该工序的连续空闲时间,如果能找到,就修改机器时间表的状态。

第三步:更新答案

每次修改机器时间表的状态时,其第二个维度表示时间,时间走到的最大值即为结果。

100分代码:

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

const int M = 25, N = 25, T = 10000;

struct Task
{
    int machine_no; //工序使用的机器号
    int times; //工序运行时长
};
Task task[N][M]; //工序信息表

struct Node
{
    int work_no; //工件号
    int seq_no; //工序号
};
Node order[M * N]; //给定的安排顺序
int seq[M * N]; //记录每个工件的第几道工序

bool machine[M][T]; //机器时间表
int last[N]; //工序的最后一次加工的时间

int m, n;

int main()
{
    cin >> m >> n;

    int x;
    //读入安排的工序顺序
    for(int i = 1; i <= m * n; i++) 
    {
        cin >> x;
        seq[x]++; //借助seq桶,计算每个工件是第几道工序

        order[i].work_no = x;  //工件号
        order[i].seq_no = seq[x]; //工序号
    }

    //读入工序所使用的机器
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
            cin >> task[i][j].machine_no;

    //读入工序运行的时长
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
            cin >> task[i][j].times;

    int ans = 0;
    for(int i = 1; i <= m * n; i++)
    {
        Node node = order[i]; //按顺序获取工序
        Task t = task[node.work_no][node.seq_no]; //获取工序信息
        int start =  ++last[node.work_no]; //工件最后一次运行的时间+1,表示该工件可以安排的起始时间,从该起始时间找到对应机器填表
        //工序的运行时长是t.times,因此需要在machine[t.machine_no][start],机器号t.machine_no,起始时间start,找到连续t.times个空位
        for(int j = start; j <= T; j++)
        {
            if(!machine[t.machine_no][j]) //当找到第一个空位时
            {
                int cnt = 0;
                int k;
                for(k = j; k <= T; k++) //用双指针找连续空位
                {
                    if(!machine[t.machine_no][k])
                    {
                        cnt++; //统计连续空位数
                        if(cnt == t.times) break;
                    }
                    else break;
                }
                if(cnt == t.times) //找到了连续t.times个空位
                {
                    for(int l = j; l < j + t.times; l++)
                    {
                        machine[t.machine_no][l] = true; //将工序填入机器时刻表
                        last[node.work_no] = l; //更新工序最后一次操作的时间
                        ans = max(ans, l); //更新答案,答案是机器时刻表中时间的最大值
                    }
                    break;
                }
                else j = k;
            }
        }
    }
    
    cout << ans;

    return 0;
}

 

posted @ 2024-01-24 12:05  五月江城  阅读(84)  评论(0编辑  收藏  举报