洛谷题单指南-模拟和高精度-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;
}