题源 湖北省宜昌市第一中学 陈凡 《冲刺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
输出应该为
9
5 2
5
而不是
9
5
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.