• 生日蛋糕 (codevs 1710) 题解


    【问题描述】

         7月17日是Mr.W的生日,ACM-THU为此要制作一个体积为Nπ的M层生日蛋糕,每层都是一个圆柱体。

    设从下往上数第i(1<=i<=M)层蛋糕是半径为Ri,高度为Hi的圆柱。当i<M时,要求Ri>Ri+1且Hi>Hi+1。

    由于要在蛋糕上抹奶油,为尽可能节约经费,我们希望蛋糕外表面(最下一层的下底面除外)的面积Q最小。

    令Q= Sπ

    请编程对给出的N和M,找出蛋糕的制作方案(适当的Ri和Hi的值),使S最小。

    (除Q外,以上所有数据皆为正整数)

    【样例输入】

        100

        2

    【样例输出】

        68

    【解题思路】

          本题为NOI1999的题目,题目的意思是让我们去搜索合适的r和h,使得s最小。我们把某一步的状态设为(i,ri,hi,si,vi),i表示从下往上数做到了第几层蛋糕,ri表示当前最上层蛋糕的半径,hi表示当前最上层蛋糕的高度,si表示当前蛋糕的表面积,vi表示做了当前蛋糕现在剩下的体积。

          于是乎,由题意可知,从(i,ri,hi,si,vi)到(i+1,r(i+1),h(i+1)s(i+1),v(i+1))满足以下条件: r(i+1)<ri;h(i+1)<hi;v(i+1)=vi-r(i+1)*r(i+1)*h(i+1);s(i+1)=si+2*r(i+1)*h(i+1);

          直接就这么搜索显然是不现实的,我们需要剪枝,那么,我们可以从下面几个方面想:

          1:当前表面积+剩余的侧面积>当前最优值,那么我们就可以剪枝了,关键在于剩余的侧面积该怎么算。这里有一个公式,是由2*vi推出来的,余下的侧面积>=2*vi/r(i+1),因此,如果2*vi/r(i+1)+si大于当前最优值,那么我们就剪枝。这个叫做最优化剪枝。

          2:若剩余的体积比做最小的蛋糕的体积还要小,就可以剪枝了,而做最小的蛋糕的体积可以用倒推法,做第m层半径为1,高也为1,那么体积为1,做第m-1层半径为2,高也为2,那么体积为8……做第i+1层就半径为m-i,高也为m-i,那么体积为(m-i)^3,因此,从1循环到m-i把体积相加,再与现在的体积比较就行了。

          3:若剩余的体积比做最大的蛋糕的体积还要大,那也可以剪枝了,方法与2相同。2与3叫做可行性剪枝。

         然而,这道题却是在逗我们……

         你做着做着会发现,剪来剪去反而一个不好就把最佳答案给剪掉了,程序也长,最终迎来的还是红色的WA,可是,数据告诉我们,只要确定好s、h可能的值,那么只要用到最优化剪枝,便可以AC了……(经本人亲身实验,搞了一上午的剪枝,每一次都把方案给剪了,最终改了一下循环的初值和终值,去掉可行性剪枝都能轻松AC……加上可行性剪枝因为多了判断的时间反而耗时多了一点点……)这里我将两种程序都贴出来,希望哪位大神看到了能够告诉我我哪里剪错了……不胜感激。

    【代码实现】

     1 uses math;
     2 var r,h,s,v,ans,n,m,i:longint;
     3 procedure search(i,r,h,s,v:longint);
     4 var a,b,c,d:longint;
     5 begin
     6  if i=m then
     7   begin
     8    if v=0 then
     9     if s<ans then
    10      ans:=s;
    11    exit;
    12   end;
    13  if s+2*v div r>ans then
    14   exit;//最优化剪枝
    15  for a:=r-1 downto m-i do//注意循环变量,自己去算一算,是可以到m-i的,而不是到i
    16   for b:=min(v div (a*a),h-1) downto m-i do
    17    begin
    18     c:=s+2*a*b;
    19     d:=v-a*a*b;
    20     search(i+1,a,b,c,d);
    21    end;
    22 end;
    23 begin
    24  readln(n);
    25  readln(m);
    26  s:=0;
    27  ans:=maxlongint;
    28  for r:=m to trunc(sqrt(n)) do
    29   for h:=n div (r*r) downto m do
    30    begin
    31     s:=2*r*h+r*r;
    32     v:=n-r*r*h;
    33     search(1,r,h,s,v);
    34    end;
    35  if ans=maxlongint then
    36   ans:=0;
    37  writeln(ans);
    38 end.
     1 uses math;
     2 var r,h,s,v,ans,n,m:longint;
     3 procedure search(i,r,h,s,v:longint);
     4 var a,b,c,d,mini,k:longint;
     5 begin
     6  mini:=0;
     7  if s+((2*v) div r)>ans then
     8   exit;//剪枝1
     9  for k:=1 to m-i do
    10   mini:=mini+k*k*k;
    11  if v<mini then
    12   exit;//剪枝2
    13  mini:=0;
    14  for k:=i+1 to m do
    15   mini:=mini+sqr(r-k+i)*(h-k+i);
    16  if v>mini then
    17   exit;//剪枝3
    18  if (i=m)and(v=0) then
    19   if s<ans then
    20    begin
    21     ans:=s;
    22     exit;
    23    end;
    24  for a:=r-1 downto i do
    25   for b:=min(v div (a*a),h-1) downto i do
    26    begin
    27     c:=s+2*a*b;
    28     d:=v-a*a*b;
    29     search(i+1,a,b,c,d);
    30    end;
    31 end;
    32 begin
    33  readln(n);
    34  readln(m);
    35  ans:=maxlongint;
    36  for r:=m to trunc(sqrt(n)) do
    37   for h:=n div (r*r) downto m do
    38    begin
    39     s:=2*r*h+r*r;
    40     v:=n-r*r*h;
    41     search(1,r,h,s,v);
    42    end;
    43  if ans=maxlongint then
    44   ans:=0;
    45  writeln(ans);
    46 end.
  • 相关阅读:
    线程安全的简单理解
    单链表 之 判断两链表是否交叉
    React组件间的通信
    input type=file美化
    Array.prototype.slice.call(arguments)
    ES5 数组方法every和some
    nodejs学习之表单提交(1)
    ES5 数组方法reduce
    ES5 数组方法map
    ES5 数组方法forEach
  • 原文地址:https://www.cnblogs.com/PengBoLiuXu/p/4511925.html
Copyright © 2020-2023  润新知