超难理解的一道题。嘴上说着是一道大模拟实际上无法理解题意就会让事情变得巨麻烦。
强烈建议画图研究样例。
原题链接:https://www.luogu.org/problem/show?pid=1065#sub
这是目前为止我所做过的难度最大的纯模拟题。一般来说,模拟题的题解文字说明都比较少,因为代码具体什么意思大家一般都能看明白,但这道题不太一样,所以我打算写的稍微多一些。
这类长模拟代码看的时间久了对一些变量可能有记忆混淆的事情发生,所以我在主要过程基本没使用简单的单字母或者双字母命名变量,那样会严重丧失可读性。
我们来分析一下题意。
题目已经给出了安排好的工序,而每个工序需要在几号机上完成以及每个工序的时间也给了出来,我们要做的就是合理安排机器的工作,让总的加工时间最短。
按照题意的约定,最短方案有且只有一种,而且不必判断输入的合法性。
我们可以把机器想成若干个「时间线」,在这条时间线上去安排工作。
那么明显的,每个时间段对应的机器就只有俩状态:
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即可。
参考代码:
1 #include <iostream>
2 #define maxn 50
3 using namespace std;
4 int n,m;
5 int ans = 0;
6 int worklist[maxn * maxn];
7 int worknumber[maxn][maxn];
8 int worktime[maxn][maxn];
9 int cnt_now_work_step[maxn];
10 int lasttime[maxn];
11 bool timeline[maxn * maxn][maxn * maxn];
12
13 bool check_in_line(int begin_time_point,int end_time_length,int workid){
14 for (int time = begin_time_point; time <= end_time_length;time++)
15 if (timeline[workid][time])
16 return false;
17 return true;
18 }
19
20 int main(){
21 cin >> m >> n;
22 for (int i=1;i<=n*m;i++)
23 cin >> worklist[i];
24
25 for (int i=1;i<=n;i++)
26 for (int j=1;j<=m;j++)
27 cin >> worknumber[i][j];
28
29 for (int i=1;i<=n;i++)
30 for (int j=1;j<=m;j++)
31 cin >> worktime[i][j];
32
33 for (int i=1;i<=n*m;i++){
34 int nowitem = worklist[i];
35 cnt_now_work_step[nowitem]++;//工序数
36 int nownumber = worknumber[nowitem][cnt_now_work_step[nowitem]];
37 int costtime = worktime[nowitem][cnt_now_work_step[nowitem]];
38
39 for (int time = lasttime[nowitem]+1;;time++)//扫描时间轴
40 if (check_in_line(time,time+costtime-1,nownumber)){
41 for (int marktime = time;marktime <= time+costtime-1;marktime++)
42 timeline[nownumber][marktime] = true;
43 lasttime[nowitem] = time + costtime - 1;
44 break;
45 }
46 }
47
48 for (int i=1;i<=n;i++)
49 ans = max(ans,lasttime[i]);
50
51 cout << ans << endl;
52
53 return 0;
54 }
看了一下时间,我从23:00开始做这道题,现在是凌晨01:40。。。。。修仙修的我脑袋疼。。
但题解还是要更的啊。更何况是这样的好题。