- 注册时间
- 2008-7-21
- 最后登录
- 1970-1-1
- 威望
- 星
- 金币
- 枚
- 贡献
- 分
- 经验
- 点
- 鲜花
- 朵
- 魅力
- 点
- 上传
- 次
- 下载
- 次
- 积分
- 11556
- 在线时间
- 小时
|
楼主 |
发表于 2009-2-25 14:16:13
|
显示全部楼层
【快餐店】
Peter最近在R市开了一家快餐店,为了招揽顾客,该快餐店准备推出一种套餐,该套餐由A个汉堡、B个薯条和C个饮料组成。价格便宜。为了提高产量,Peter从著名的麦当劳公司引进了N条生产线。所有的生产线都可以生产汉堡、薯条和饮料,由于每条生产线每天所能提供的生产时间是有限的、不同的,而汉堡、薯条和饮料的单位生产时间又不同。这使得Peter很为难,不知道如何安排生产才能使一天中生产的套餐产量最大。请你编一程序,计算一天中套餐的最大生产量。为简单起见,假设汉堡、薯条和饮料的日产量不超过100个。
【输入格式】
输入文件共四行。第一行为三个不超过100的正整数A B C,分别表示该套餐汉堡、薯条和饮料的数量,中间以一个空格分开。第二行为3个不超过100的正整数P1,P2,P3分别为汉堡、薯条和饮料的单位生产耗时,中间以一个空格分开。第三行为生产线条数N(0≤N≤10),第四行为N个不超过10000的正整数,分别为各条生产流水线每天提供的生产时间,中间以一个空格分开。
【输出格式】
输出文件仅一行,即每天套餐的最大产量。
【输入样例】
2 2 2
1 2 2
2
6 6
【输出样例】
1
【二】分析
本题是一个非常典型的资源分配问题。由于每条生产线的生产是相互独立,不互相影响的,所以此题可以以生产线为阶段用动态规划求解。
状态表示: 用p[I,j,k]表示前I条生产线生产j个汉堡,k个薯条的情况下最多可生产饮料的个数。
用r[I,j,k]表示第I条生产线生产j个汉堡,k个薯条的情况下最多可生产饮料的个数。
态转移方程 : p[I,j,k] = Max{p[I-1,j1,k1]+r[I,j-j1,k-k1]}
(0<=j1<=j,0<=k1<=k,且(j-j1)*p1+(k-k1)*p2<= 第I条生产线的生产时间)
但这样的算法是非常粗糙的,稍加分析可以发现,此算法的时间复杂度为O(N*1004),当N=10的时候,时间复杂度将达到10*1004=109,这是根本无法承受的。
于是,我们必须对算法进行进一步的优化,经仔细观察可以发现:这道题中存在着一个上限值(Min{100 div A, 100 div B, 100 div C}),这是一个很好的判断条件,他可以帮我们尽早地求出最优解。为什么这样说呢?因为,在动态规划开始前,我们可以先用贪心法算出N条生产线可以生产的套数,如果大于上限值则可以直接输出上限值并退出。否则再调用动态规划,而在动态规划求解的过程中,也可以在每完成一阶段工作便和上限值进行比较,若大于等于上限值就退出,这样一来,就可以尽早的求出解并结束程序。
具体的算法流程为:
【三】小结
动态规划虽然是种高效的算法,但不加优化的话,有可能在时间和空间上仍然有问题,因此,在做题时要充分发掘题目中的隐含条件,并充分利用已知条件,这样才能令程序更快,更优。
【四】对程序优化的进一步讨论:
本题在具体实现中还有一些优化的余地,在此提出,以供参考:
(1) 存储结构:由于每一阶段状态只与上一阶段状态有关,所以我们可以只用两个100*100的数组滚动实现。但考虑到滚动是有大量的赋值,可以改进为动态数组,每次交换时只需交换指针即可,这样比原来数组间赋值要快。
(2) 减少循环:在计算每一阶段状态过程中无疑要用到4重循环,其实这当中有很多是可以省掉的,我们可以这样修改每一重循环的起始值:
原起始值: 改进后的起始值:
for I := 1 to n do begin for I := 1 to n do begin
for j := 0 to tot[I] div p1 do for j := 0 to min(q1,tot[I] div p1) do
for k := 0 to (tot[I]-p1*j) div p2 do for k := 0 to min(q2,(tot[I]-p1*j) div p2) do
for j1:=0 to j do for j1 := max(0,j-t[I] div p1) to
min(j,tot[I-1] div p1) do
for k1 := 0 to k do for k1 := max(0,k-(t[I]-(j-j1)*p1) div p2)
to min(k,(tot[I-1]-p1*j1)div p2) do
{ 注:具体变量用途请参考程序 }
【五】参考程序- {\$A+,B-,D+,E+,F-,G-,I+,L+,N-,O-,P-,Q+,R+,S+,T-,V+,X+,Y+}
- {\$M 65520,0,655360}
-
- program FastFood;
- const
- input = 'input2.txt';
- output = 'output2.txt';
- var
- f : text;
- r,p,pp : array[0..100,0..100] of integer; {pp:滚动数组中存放前一阶段的数组}
- t,tot,tt : array[0..10] of longint; {tt:辅助数组;t:每条生产线的生产线时间;
- tot:1-I条生产线生产时间的总和}
- n,a,b,c,p1,p2,p3 : longint; {a,b,c:汉堡,薯条,饮料的个数;
- p1,p2,p3汉堡,薯条,饮料的生产单位耗时}
-
- procedure init;
- var
- i : integer;
- begin
- assign(f,input);
- reset (f);
- readln(f,a,b,c);
- readlN(f,p1,p2,p3);
- readln(f,n);
- for i := 1 to n do read(f,t[i]);
- close (f);
- if n=0 then begin { 特殊情况处理 }
- assign(f,output);
- rewrite(f);
- writeln(f,0);
- close (f);
- halt;
- end;
- end;
-
- function min(i,j : longint) : longint; {求两数中较小的一个}
- begin
- if i>j then min := j else min := i;
- end;
-
- function max(i,j : longint) : longint; {求两数中较大的一个}
-
- begin
- if i>j then max := i else max := j;
- end;
-
- procedure main;
- var
- q,q1,q2,i,j,k,j1,k1 : longint; {q:上限值;q1,q2 : A,B的上限值}
- begin
- q := min( 100 div a,min( 100 div b, 100 div c ) ); {求上限值}
- q1 := q*a;
- q2 := q*b;
- tt := t;
- i := 1;
- j := q1*p1;
- while (j>0) and (i<=n) do { 用贪心法求出N条生产线可以生产的套数(可行解)}
- if j>tt[i] then begin
- dec(j,tt[i]); tt[i] := 0; inc(i);
- end
- else begin dec(tt[i],j); j := 0; end;
- if j=0 then begin
- j := q2*p2;
- while (j>0) and (i<=n) do
- if j>tt[i] then begin
- dec(j,tt[i]) ;tt[i] := 0; inc(i);
- end
- else begin dec(tt[i],j); j := 0; end;
- if j=0 then begin {如果可行,直接输出上限值}
- assign(f,output);
- rewrite(f);
- writeln(f,q);
- close (f);
- halt;
- end;
- end;
- tot[0] := 0;
- for i := 1 to n do tot[i] := tot[i-1] + t[i];
- if tot[n] div (a*p1+b*p2+c*p3)<q then begin {否则修改上限值}
- q := tot[n] div (a*p1+b*p2+c*p3);
- q1 := q*a;
- q2 := q*b;
- end;
- for i := 1 to n do begin
- for j := 0 to min(q1,t[i] div p1) do
- for k := 0 to min(q2,(t[i]-p1*j) div p2) do
- r[j,k] := (t[i]-p1*j-p2*k) div p3;
- fillchar(p,sizeof(p),0); {数组清零}
- for j := 0 to min(q1,tot[i] div p1) do
- for k := 0 to min(q2,(tot[i]-p1*j) div p2) do
- for j1 := max(0,j-t[i] div p1) to min(j,tot[i-1] div p1) do
- for k1 := max(0,k-(t[i]-(j-j1)*p1) div p2) to min(k,(tot[i-1]-p1*j1) div p2) do
- if p[j,k] < pp[j1,k1] + r[j-j1,k-k1] then
- p[j,k] := pp[j1,k1] + r[j-j1,k-k1];
- if p[q*a,q*b]>=q*c then begin
- {如果在此阶段产生了不小于上限值的解,则之际输出上限值,并直接退出}
- assign(f,output);
- rewrite(f);
- writeln(f,q);
- close (f);
- halt;
- end;
- pp := p;
- for j := 0 to min(100,tot[i] div p1) do
- for k := 0 to min(100,(tot[i]-p1*j) div p2) do
- if p[j,k] > 100 then p[j,k] := 100;
- end;
- { out }
- k1 := 0; { 输出最优值 }
- for j := 0 to 100 do
- if (j div a > k1) then
- for k := 0 to 100 do
- if (k div b > k1) and (p[j,k] div c > k1 ) then
- k1 := min( min( j div a, k div b), p[j,k] div c );
- assign(f,output);
- rewrite(f);
- writeln(f,k1);
- close (f);
- end;
-
- begin
- init;
- main;
- end.
复制代码 |
|