| 
注册时间2008-7-21最后登录1970-1-1威望 星金币 枚贡献 分经验 点鲜花 朵魅力 点上传 次下载 次积分11560在线时间 小时 
 | 
 
 楼主|
发表于 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.
 | 
 |