P1065 [NOIP2006 提高组] 作业调度方案题目

2024-01-26 04:12

本文主要是介绍P1065 [NOIP2006 提高组] 作业调度方案题目,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

题目

我们现在要利用m台机器加工n个工件,每个工件都有m道工序,每道工序都在不同的指定的机器上完成。每个工件的每道工序都有指定的加工时间。

每个工件的每个工序称为一个操作,我们用记号j-k表示一个操作,其中j为1到n中的某个数字,为工件号;k为1到m中的某个数字,为工序号,例如2-4表示第2个工件第4道工序的这个操作。在本题中,我们还给定对于各操作的一个安排顺序。

例如,当n=3,m=2时,1-1,1-2,2-1,3-1,3-2,2-2就是一个给定的安排顺序,即先安排第1个工件的第1个工序,再安排第1个工件的第2个工序,然后再安排第2个工件的第1个工序,等等。

一方面,每个操作的安排都要满足以下的两个约束条件。

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

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

另一方面,在安排后面的操作时,不能改动前面已安排的操作的工作状态。

由于同一工件都是按工序的顺序安排的,因此,只按原顺序给出工件号,仍可得到同样的安排顺序,于是,在输入数据中,我们将这个安排顺序简写为1 1 2 3 3 2

还要注意,“安排顺序”只要求按照给定的顺序安排每个操作。不一定是各机器上的实际操作顺序。在具体实施时,有可能排在后面的某个操作比前面的某个操作先完成。

例如,取n=3,m=2,已知数据如下(机器号/加工时间):

工件号工序 1工序 2
11/32/2
21/22/5
32/21/4

则对于安排顺序1 1 2 3 3 2,下图中的两个实施方案都是正确的。但所需要的总时间分别是10与12。

方案1,用时10:

时间12345678910
机器1执行工序1-11-11-12-12-13-23-23-23-2
机器2执行工序3-13-11-21-22-22-22-22-22-2

方案2,用时12:

时间123456789101112
机器1执行工序1-11-11-12-12-13-23-23-23-2
机器2执行工序1-21-23-13-12-22-22-22-22-2

当一个操作插入到某台机器的某个空档时(机器上最后的尚未安排操作的部分也可以看作一个空档),可以靠前插入,也可以靠后或居中插入。为了使问题简单一些,我们约定:在保证约束条件(1.)(2.) 的条件下,尽量靠前插入。并且,我们还约定,如果有多个空档可以插入,就在保证约束条件(1.)(2.)的条件下,插入到最前面的一个空档。于是,在这些约定下,上例中的方案一是正确的,而方案二是不正确的。

显然,在这些约定下,对于给定的安排顺序,符合该安排顺序的实施方案是唯一的,请你计算出该方案完成全部任务所需的总时间。

输入输出格式

输入格式

第1行为两个正整数m, n,用一个空格隔开, 其中m(<20) 表示机器数,n(<20)表示工件数。

第2行:m×n个用空格隔开的数,为给定的安排顺序。

接下来的2n行,每行都是用空格隔开的m个正整数,每个数不超过20。

其中前n行依次表示每个工件的每个工序所使用的机器号,第1个数为第1个工序的机器号,第2个数为第2个工序机器号,等等。

后n行依次表示每个工件的每个工序的加工时间。

可以保证,以上各数据都是正确的,不必检验。

输出格式

1 个正整数,为最少的加工时间。

输入输出样例

输入样例

2 3
1 1 2 3 3 2
1 2 
1 2 
2 1
3 2 
2 5 
2 4

输出样例

10

解析

题目已经给出了安排好的工序,而每个工序需要在几号机上完成以及每个工序的时间也给了出来,我们要做的就是合理安排机器的工作,让总的加工时间最短。

按照题意的约定,最短方案有且只有一种,而且不必判断输入的合法性。

我们可以把机器想成若干个「时间线」,在这条时间线上去安排工作。

那么明显的,每个时间段对应的机器就只有俩状态:1.我在干活     2.我闲着呢

而每一个工件也有自己的加工要求,对于每个工件的工序,总应该先完成小号工序再完成大号工序,也就是必须顺着编号来。

每台机器只能在某时刻进行一种工作,并且后面的安排不能把前面的安排改动掉。

模拟的思想便是从左到右无限扫描整个时间线,然后去尝试插空。

有三个辅助数组,第一个是cnt_now_work_step,它表示当前取到工件的工序数。根据之前输入的workline,每个数都代表一个安排的工序,这个数组就是用来方便后面处理工序的。第二个是lasttime,它代表某个工件出现的最晚的时间(点),它可以用来方便我们扫描时间线,因为每一个工件必须要完全完成上一道工序后才能接着继续下一道工序。第三个是二维bool数组timeline,它代表某一台机器在某一个时间(点)上是不是正在干活。

有了这三个辅助数组,我们就可以开始按照模拟的思路写代码了。

我们取当前工件nowitem[i],让cnt_now_work_step[nowitem]++,即代表这个工件的工序+1,用nownumber记录当前工件在当前工序时位于哪一台机器,costtime表示做完这道工序应该花费的时间,lasttime[nowitem]+1便是我们扫描时间线的开端,注意lasttime记录的是时间点。这个for没有终止条件,因为时间轴可能会无穷远。

接下来是关键,判断从这个时间点到干完这道工序,机器有没有空,如果机器表示“我闲着呢”,那么就把这道工序安排给机器的这个时间段,更新timeline和lasttime(lasttime[nowitem] = time + costtime - 1,干完活之后这个工件出现的最晚的时间点应该是这道新工序做完的那一刻),然后更新操作立即break掉,继续扫描时间线。如果机器表示“这个时间段我在干其他活”,那这个任务就不能放在这一段,时间线继续扫描。

这个判断这里用一个函数来实现,它传入起始时间点和终止时间点和工件编号,然后去判断它的timeline。循环完所有的工件,整个的时间轴也就确定了。最后去寻找ans,ans应该等于值最大的那个lasttime(即这个工件最后才做完)。输出ans即可。

代码

#include<iostream>
#define maxn 100
using namespace std;
int n,m;//定义工件数和机器数 
int ans = 0;//最后输出的结果 
int worklist[maxn * maxn];
int worknumber[maxn][maxn];
int worktime[maxn][maxn];
int cnt_now_work_step[maxn];
int lasttime[maxn];
bool timeline[maxn * maxn][maxn * maxn];//某一台机器在某一个时间(点)上是不是正在干活bool check_in_line(int begin_time_point,int end_time_length,int workid){for (int time = begin_time_point; time <= end_time_length;time++)if (timeline[workid][time])return false;return true;    
}//判断机器是不是空闲 int main(){cin >> m >> n;//输入机器数和工件数 for (int i=1;i<=n*m;i++){ cin >> worklist[i];//输入给定的安排顺序 } for (int i=1;i<=n;i++){ for (int j=1;j<=m;j++){ cin >> worknumber[i][j];//每一工件每个工序的机器号 }} for (int i=1;i<=n;i++){ for (int j=1;j<=m;j++){ cin >> worktime[i][j];//每个工件每个工序的加工时间 }} for (int i=1;i<=n*m;i++){int nowitem = worklist[i];//工件的安排顺序 cnt_now_work_step[nowitem]++;//当前取到工件的工序数int nownumber = worknumber[nowitem][cnt_now_work_step[nowitem]];//当前工件在当前工序时位于哪一台机器 int costtime = worktime[nowitem][cnt_now_work_step[nowitem]];//当前工件做完当前工序的加工时间 for (int time = lasttime[nowitem]+1;;time++)//扫描时间轴if (check_in_line(time,time+costtime-1,nownumber)){for (int marktime = time;marktime <= time+costtime-1;marktime++)timeline[nownumber][marktime] = true;lasttime[nowitem] = time + costtime - 1;break;}}for (int i=1;i<=n;i++)ans = max(ans,lasttime[i]);cout << ans << endl;return 0;
}

这篇关于P1065 [NOIP2006 提高组] 作业调度方案题目的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/645607

相关文章

Java中的xxl-job调度器线程池工作机制

《Java中的xxl-job调度器线程池工作机制》xxl-job通过快慢线程池分离短时与长时任务,动态降级超时任务至慢池,结合异步触发和资源隔离机制,提升高频调度的性能与稳定性,支撑高并发场景下的可靠... 目录⚙️ 一、调度器线程池的核心设计 二、线程池的工作流程 三、线程池配置参数与优化 四、总结:线程

MySQL 迁移至 Doris 最佳实践方案(最新整理)

《MySQL迁移至Doris最佳实践方案(最新整理)》本文将深入剖析三种经过实践验证的MySQL迁移至Doris的最佳方案,涵盖全量迁移、增量同步、混合迁移以及基于CDC(ChangeData... 目录一、China编程JDBC Catalog 联邦查询方案(适合跨库实时查询)1. 方案概述2. 环境要求3.

SpringBoot3.X 整合 MinIO 存储原生方案

《SpringBoot3.X整合MinIO存储原生方案》本文详细介绍了SpringBoot3.X整合MinIO的原生方案,从环境搭建到核心功能实现,涵盖了文件上传、下载、删除等常用操作,并补充了... 目录SpringBoot3.X整合MinIO存储原生方案:从环境搭建到实战开发一、前言:为什么选择MinI

Knife4j+Axios+Redis前后端分离架构下的 API 管理与会话方案(最新推荐)

《Knife4j+Axios+Redis前后端分离架构下的API管理与会话方案(最新推荐)》本文主要介绍了Swagger与Knife4j的配置要点、前后端对接方法以及分布式Session实现原理,... 目录一、Swagger 与 Knife4j 的深度理解及配置要点Knife4j 配置关键要点1.Spri

SQLite3 在嵌入式C环境中存储音频/视频文件的最优方案

《SQLite3在嵌入式C环境中存储音频/视频文件的最优方案》本文探讨了SQLite3在嵌入式C环境中存储音视频文件的优化方案,推荐采用文件路径存储结合元数据管理,兼顾效率与资源限制,小文件可使用B... 目录SQLite3 在嵌入式C环境中存储音频/视频文件的专业方案一、存储策略选择1. 直接存储 vs

SpringBoot服务获取Pod当前IP的两种方案

《SpringBoot服务获取Pod当前IP的两种方案》在Kubernetes集群中,SpringBoot服务获取Pod当前IP的方案主要有两种,通过环境变量注入或通过Java代码动态获取网络接口IP... 目录方案一:通过 Kubernetes Downward API 注入环境变量原理步骤方案二:通过

Springboot3+将ID转为JSON字符串的详细配置方案

《Springboot3+将ID转为JSON字符串的详细配置方案》:本文主要介绍纯后端实现Long/BigIntegerID转为JSON字符串的详细配置方案,s基于SpringBoot3+和Spr... 目录1. 添加依赖2. 全局 Jackson 配置3. 精准控制(可选)4. OpenAPI (Spri

关于跨域无效的问题及解决(java后端方案)

《关于跨域无效的问题及解决(java后端方案)》:本文主要介绍关于跨域无效的问题及解决(java后端方案),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录通用后端跨域方法1、@CrossOrigin 注解2、springboot2.0 实现WebMvcConfig

在Java中将XLS转换为XLSX的实现方案

《在Java中将XLS转换为XLSX的实现方案》在本文中,我们将探讨传统ExcelXLS格式与现代XLSX格式的结构差异,并为Java开发者提供转换方案,通过了解底层原理、性能优势及实用工具,您将掌握... 目录为什么升级XLS到XLSX值得投入?实际转换过程解析推荐技术方案对比Apache POI实现编程

Java使用MethodHandle来替代反射,提高性能问题

《Java使用MethodHandle来替代反射,提高性能问题》:本文主要介绍Java使用MethodHandle来替代反射,提高性能问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑... 目录一、认识MethodHandle1、简介2、使用方式3、与反射的区别二、示例1、基本使用2、(重要)