1441:【例題2】生日蛋糕
時間限制: 1000 ms 內存限制: 65536 KB
提交數: 997 通過數: 502
【題目描述】
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=πR^2H
側面積A=2πRH
底面積A=πR^2
思路:dfs三重剪枝
當前表面積+下一層表面積如果超過最優值就退出
當前體積+下一層體積如果超過總體積就退出
假設剩餘所有的體積都用來做下一層那麼此時下一層的體積是最大 而半徑會最大 從而表面積最小(當體積一定時 半徑越大 表面積越小)
每次枚舉半徑和高時 是從下一層的半徑和高到還剩下的層數 因爲每層都要比下面大1
最優化剪枝
剪枝:
s1+2*(n-v1)/r > ans 通過以下式子得出:
前d層蛋糕的體積 v=r[1]*r[1]*h[1]+…+r[d]*r[d]h[d] < rr[1]h[1]+…+rr[d]*h[d]
(這裏r是最底層蛋糕半徑)
顯然n-v< r*r[1]*h[1]+...+r*r[d]*h[d]
兩邊同時/r *2
那麼左邊 2*(n-v)/r
右邊就是 2*r[1]*h[1]+...+2*r[d]*h[d] ,也就是之前記錄的前d層表面積 ans
也就是要保證 s1+2*(n-v)/r <ans ,纔有較優解
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#define N 30
#define INF 0x3f3f3f3f
using namespace std;
int m,n,ans = INF;
int v[N],s[N];
void dfs(int cur,int s1,int v1,int r,int h)//cur爲當前層數 s1爲已經有的表面積 v1爲已經有的體積,r爲當前半徑 h爲當前高度
{
if(cur == 0)//如果到頂層
{
if(v1 == n && s1 < ans)//且體積滿足題目要求 並且表面積小於原來的最優
ans = s1;
return;
}
if(s1+s[cur] > ans) return;//當前表面積+下一層表面積如果超過最優值就退出
if(v1+v[cur] > n) return;//當前體積+下一層體積如果超過總體積就退出
if(s1+2*(n-v1)/r > ans) return;//假設剩餘所有的體積都用來做下一層那麼此時下一層的體積是最大,而半徑會最大,從而表面積最小
for(int i = r - 1;i >= cur;i--)//從下一層最小半徑開始枚舉尋找合適的半徑
{
if(cur == m) s1 = i*i;//如果是底層 表面積爲“頂面 ”
int h1 = min(h-1,(n-v1-v[cur-1])/(i*i));//總體積-已用體積-下一層體積除底面積爲高與下一層最大高度比較
for(int j = h1; j >= cur;j--)
{
dfs(cur-1,s1+2*i*j,v1+i*i*j,i,j);
}
}
}
int main(){
cin >> n >> m;
for(int i = 1; i <= m; i++)
{
v[i] = v[ i-1] +i*i*i;//前i層+自身的最大體積
s[i] = s[i-1] + 2*i*i; //i層+自身的最大表面積
}
dfs(m,0,0,sqrt(n),n);
if(ans == INF)
cout << 0;
else
cout << ans;
}