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外,以上所有数据皆为正整数)
设从下往上数第i(1 <= i <= M)层蛋糕是半径为Ri, 高度为Hi的圆柱。当i < M时,要求Ri > Ri+1且Hi > Hi+1。
由于要在蛋糕上抹奶油,为尽可能节约经费,我们希望蛋糕外表面(最下一层的下底面除外)的面积Q最小。
令Q = Sπ
请编程对给出的N和M,找出蛋糕的制作方案(适当的Ri和Hi的值),使S最小。
(除Q外,以上所有数据皆为正整数)
Input
有两行,第一行为N(N <= 10000),表示待制作的蛋糕的体积为Nπ;第二行为M(M <= 20),表示蛋糕的层数为M。
Output
仅一行,是一个正整数S(若无解则S = 0)。
Sample Input
100 2
Sample Output
68
Hint
圆柱公式
体积V = πR 2H
侧面积A' = 2πRH
底面积A = πR 2
体积V = πR 2H
侧面积A' = 2πRH
底面积A = πR 2
思路:正常深搜,但是会TLE,需要众多剪枝
①我们可以预处理1~当前层所需的最小表面积和体积,然后如果已经选的表面积(体积)+ 剩下最小表面积(体积)超过ans(N规定体积)就return
②枚举r,h时,我们可以知道规定枚举范围,就不需要每次减一递减
maxR = min(r,sqrt(N-SumV-minV[now-1])
maxH = min(h,(n-SumV-minV[now-1])/(i*i))
③最难的一个剪枝:
n-SumV = Σh【k】*r【k】*r【k】 (1<=k<=now)
2*Σr【k】*h【k】= 2/r【now+1】*Σr【k】*h【k】*r【now+1】 >= 2/r【now+1】*Σh【k】*r【k】*r【k】(1<=k<=now)
2*Σr【k】*h【k】 >= 2*(n-SumV)/r【now+1】 (1 <= k <= n)
所以 SumS + = 2*(n-SumV)/r【now+1】 >= ans 就return
因为当前dfs的r,h是本次选择的时候的边界,所以加个last变量记录r【now+1】即上次选择的半径r
#include<cstdio> #include<cstdio> #include<algorithm> #include<math.h> using namespace std; int n,m,ans; int minV[22]; int minS[22]; void dfs(int now,int SumS,int SumV,int r,int h,int last) { if(SumS + minS[now] > ans) return; if(SumV + minV[now] > n) return; if(SumS + 2*(n-SumV)/last >= ans) return; if(!now) { if(SumV == n && SumS < ans) ans = SumS; return; } int maxR = min(r,(int)sqrt(n-SumV-minV[now-1])); for(int i=maxR;i>=now;i--) { if(now == m)SumS = i*i; int maxH = min((n-minV[now-1]-SumV)/(i*i), h); for(int j=maxH;j>=now;j--) { dfs(now-1,SumS+2*i*j,SumV+i*i*j,i-1,j-1,r); } } } int main() { ans = 0x3f3f3f3f; for(int i=1; i<=18; i++) { minV[i] += minV[i-1] + i*i*i; minS[i] += minS[i-1] + 2*i*i; } scanf("%d%d",&n,&m); dfs(m,0,0,100,10000,200); if(ans == 0x3f3f3f3f)ans = 0; printf("%d ",ans); }