NOI 1999-3 生日蛋糕 解題報告

生日蛋糕

Birthday Cake

Cake.{pas|bas|c}

Cake.exe

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外,以上所有數據皆為正整數)

輸入

有兩行,第一行為N(N<=10000),表示待制作的蛋糕的體積為Nπ;第二行為M(M<=20),表示蛋糕的層數為M。

輸出

僅一行,是一個正整數S(若無解則S=0)。

樣例輸入

100 2

樣例輸出

68

附:圓柱公式

體積V=πR2H

側面積A’=2πRH

底面積A=πR2

搜索題。

但是,樸素的搜索面臨大數據超時的危機-_-||

所以,剪枝是必要的:

  • 最優化剪枝

    • k層蛋糕的最小表面積可知(h,r都為整數)

    • 當前s可知

    • 那麼,s+剩下層數最小表面積>=min最無需再往下搜了

  • 可行性剪枝

    • k層蛋糕的最小體積minv可知

    • k層蛋糕的最大體積maxv可知

    • 那麼,剩下的體積v不在minv至maxv的範圍內,即可停止搜索

最優化剪枝數據越大功力越強..^-^

於是便可以寫出程序了.

下面是我的程序,並沒有加所有的剪枝……不過AC足夠了

//NOI1999-3: CAKE 
//2009-1-23: BY Ceeji 
//Type     : Search 
program noi1999_3; 
var n,m,i,j,k,l,q,max:longint; 
    mint,mins:array[0..20]of longint; 
procedure gettijimin; 
var i,j:longint; 
begin 
     j:=0;   mint[1]:=0; mint[0]:=0; 
     for i:=1 to m-1 do 
     begin 
          inc(j,i*i*i); 
          mint[i+1]:=j; 
     end; 
end; 
procedure getsmin; 
var i,j:longint; 
begin 
     j:=0;   mins[1]:=0; mins[0]:=0; 
     for i:=1 to m-1 do 
     begin 
          inc(j,2*i*i); 
          mins[i+1]:=j; 
     end; 
end; 
procedure sou(i,v,s,h,r:longint); // i: ceng shu; v:ti ji; s:mian ji; h:max {height}; 
var j,k,l,q:longint; 
begin 
     if (i=m+1)and(v=0)and(max>s)then 
     begin 
          max:=s; exit; 
     end; 
     if (i>m)or(v< =0) then exit; 
     for j:=h downto m-i+1 do 
     begin 
         for q:=r downto m-i+1 do 
         begin 
              if (i=m)and(q*q*j<>v)then continue; 
              if s+2*q*j+mins[m-i+1]>=max then continue; 
              if i<>1 then sou(i+1,v-q*q*j,s+2*q*j,j-1,q-1) 
                      else sou(i+1,v-q*q*j,s+2*q*j+q*q,j-1,q-1); 
         end; 
     end; 
end; 
begin 
     readln(n,m); 
     max:=100000000; 
     gettijimin; getsmin; 
     k:=mint[m]; 
     if n-k< =0 then begin writeln('0'); exit; end; 
     sou(1,n,0,trunc((n-k)/(m*m)),trunc(sqrt((n-k)/m))); 
     if max=100000000 then writeln('0') else writeln(max); 
end. 
当前页阅读量为: