第1部分 基礎算法(提高篇)--第3章 深搜的剪枝技巧1441:【例題2】生日蛋糕

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;
 }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章