工件调度

题源 湖北省宜昌市第一中学 陈凡  《冲刺NOIP2008模拟题12》

【问题描述】 

Jam的工厂新进了一批货,需要赶紧加工,工厂有许多加工机器,对于这些货物来说是绰绰有余了,但是他想合理安排加工方式,使每个加工机器都被充分利用。例如:他希望工件在 8小时内加工完成,已知这里有 5个工件,按顺序,需要消耗的时间依次为 5 2 4 4 3,即 5 个工件要在 8小时内完工。当然,你可以调度它们分别在 5台加工机器上完成,但是每台机器就依次有 3 6 4 4 5小时是空闲的,那么机器的加工利用率就很低了。这里我们用剩余时间的平方和代表你调度的加工利用率,即32+62+42+42+52=102,当然,Jam希望这个值越小越好,这样才能使机器被充分利用。另外同一个机器加工完一个工件后需要休息一个小时,工件的加工是按输入所给定的顺序进行的。

请注意:

1.加工的顺序是给定的不能把后面的工件拿到前面去加工,如图中的 2和 3不能调换位置,只能依次按给定的顺序调度。这一点请引起注意!

 2.休息时间为 l 小时,对于这个例子,如果 Jam要求在 7个小时内完成,则 5号工件就必须在另一个机器上完成。当然,题目保证 Jam要求韵时间一定比消耗最长时间的工件要长,否则就无解了。 

3.你可以视机器的数量是无限的,对于一个调度,你想要几个机器加工都可以,只要总效率最高,也就是效率值最小。

4.任何工件都应该向前安排,只能在工件完成后才留出空闲时间,这一点,图中也有体现。 

【输入文件】(work.in) 

输入文件的第一行分别是Jam要求完工的时间和工件的数量第二行分别为一号工件所需时间,二号工件所需时间…输入顺序就是给定顺序 

【输出文件】(work.out)

输出文件为两行,第一行为最小的效率值,第二行至以下为调度方案。 

【输入样例】 

8 5

5 2 4 4 3 

【输出样例】 

10

5

2 4 

4 3 

【注释】输出的方案应使工件尽量向前调度,比如:

对于输入 

8 3

5 2

5

输出应该为 

5 2 

而不是 

9

2  5

【数据规模】

 对于 40%的数据有: 工件数量<10 Jam 要求时间<30

对于 100%的数据有: 工件数量<500 Jam 要求时间<30000

数据保证效率值在长整型范围内​

​ 【算法解析】

简单的说就是区间DP(记忆化搜索也可以啦,就是不太擅长搜....)

​​​​​因为工件的顺序不能打乱,所以​对于每一台机器来说,它所加工的工件一定是连续的某个区间。这样的特点可以想到一个预处理,sum[i,j]表示第i到第j个工件的加工时间。这是一个比较常用的预处理方法,当一个常用程序积木块收起来叭...

思考一下状态。f[i][j]表示共加工了i个工件,使用了j台机器的最优值。​​f[i][1]的情况显然是可以全部先预处理出来的(预处理真的好多.....)​顺便说一句,由于所要求的是最小值所以初始化别偷懒嗷....

可以用1台机器解决的情况已经处理好了,然后枚举,所需机器数,最后一台机器加工区间的起点和长度。显然,​由于每台机器至少加工一个工件(谁那么傻把机器空着耗电....),所以机器数最大为n。需要注意的是,机器数的枚举要放在最外层,不要习惯性的把j循环放在i内,因为​机器序号其实就相当于区间顺序。​

代码走一波...

var  i,j,k,n,m,x,y,p,num,ans,time,t:longint;

       a:array[0..507] of longint;

       sum,f,g,s:array[0..507,0..507]of longint;

//f[i,j]表示到工件i,用了j台机器的最低加工利用率,g[i,j]存储区间长度,便于输出 

procedure dfs(x,len:longint);//把工件按最低加工利用率的分配方法分开

var i,j:longint;

begin

     if (x=0)or(len=0) then exit;

     dfs(x-g[x,len],len-1);

     inc(num);  j:=0;

     for i:=x-g[x,len]+1 to x do  begin   inc(j);  s[num,j]:=a[i];  end;

     s[num,0]:=g[x,len];

end;

begin

      readln(time,n);  //time表示每个机器工作时间,n表示工件数

      for i:=1 to n do read(a[i]);

      for i:=1 to n-1 do 

        for j:=i to n do  sum[i,j]:=sum[i,j-1]+a[j];   //预处理sum数组,sum[i,j]表示从i到j加工所需的时间

      sum[n,n]:=a[n];



      fillchar(f,sizeof(f),$7f);

      f[0,0]:=0;

      for i:=1 to n do f[0,i]:=0;

        for i:=1 to n do

          if sum[1,i]+i-1<=time

           then begin  f[i,1]:=sqr(sum[1,i]+i-1-time);  g[i,1]:=i;  end

           else break;

      for j:=2 to n do //一共用了多少台机器

        for i:=j to n do  //当前工件区间的终点

          for k:=1 to i-j+1 do //当前工件区间的长度

            if sum[i-k+1,i]+k-1<=time then //判断当前工件区间能否放在一个机器上

             begin

              t:=f[i-k,j-1]+sqr(sum[i-k+1,i]+k-1-time);

              if (t<f[i,j])or(f[i,j]=t)and(g[i,j]>k)  then begin f[i,j]:=t; g[i,j]:=k; end;

                   //因为题目说输出的方案应使工件尽量向前调度,所以后面的长度尽量短

             end;

      ans:=maxlongint;

     p:=0;

     for i:=1 to n do

       if ans>f[n,i]  then begin  ans:=f[n,i];  p:=i; end;

     writeln(ans);

     dfs(n,p);

     for i:=1 to num do

       begin

       for j:=1 to s[i,0] do write(s[i,j],' ');

        writeln;

        end;

end.​

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值