生日蛋糕
Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 25000 | Accepted: 8945 |
Description
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外,以上所有数据皆为正整数)
Input
有两行,第一行为N(N <= 10000),表示待制作的蛋糕的体积为Nπ;第二行为M(M <= 20),表示蛋糕的层数为M。
Output
仅一行,是一个正整数S(若无解则S = 0)。
Sample Input
100 2
Sample Output
68
Hint
圆柱公式
体积V = πR2H
侧面积A' = 2πRH
底面积A = πR2
代码主体部分并不是我的,我只是站在巨人的肩膀上加了一些自己的理解
以帮助大家更好地学习
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#define INF 0x3f3f3f3f
using namespace std;
const int MAXM = 23;
const int MAXN = 1e4+10;
int lminQ[MAXM], lminV[MAXM];
int min_Q;
int N, M;
///已经凑的体积,已经凑的表面积,当前在第几层,下一层的半径,下一层的高
void dfs(int V, int Q, int step, int r, int h)
{
if(step == 0) ///层数用完
{
if(V == N) min_Q = min(min_Q, Q); ///体积刚刚好
return;
}
if(V+lminV[step] > N||Q+lminQ[step] >= min_Q) return; //剪枝:如果当前体积加上理论最小体积超过N或者当前表面积加理论最小表面积超过最优值
/*
原句是if(V+lminV[step] > N || Q+lminQ[step] >= min_Q),但是会发现后半句的范围比下面的少
因为lmin只是规定了高为1时候的最小值,换句话说,当符合Q+lminQ[step] >= min_Q的条件会更少一些
所以下一句if(2*(N-V)/r + Q >= min_Q) return的优化程度很大,因为它考虑了当前的实际情况
当然加上以后更快一些
*/
if(2*(N-V)/r + Q >= min_Q) return; //剪枝:假设最小的表面积已经大于等于最优值,则没有继续搜的意义了
///其中Q是最底层的面积,因为俯视来看,上面都是需要刷的,而那些面积等同于底面积
int max_R = r-1; ///最大半径为上一层半径减一
for(int i = max_R; i >= step; i--) ///枚举半径
{
if(step == M) ///当前在最底层
{
Q = i*i; ///表面积加上最底层的底面积
}
int max_H = h-1; ///最大的高度
for(int j = max_H; j >= step; j--)
{
dfs(V+i*i*j, Q+2*i*j, step-1, i, j);
}
}
}
int main()
{
scanf("%d%d", &N, &M);
lminQ[0] = 0, lminV[0] = 0;
for(int i = 1; i < 22; i++) ///预处理每一层的理论最小值
{
lminQ[i] = lminQ[i-1] + 2*i*i;
lminV[i] = lminV[i-1] + i*i*i;
}
min_Q = INF;
dfs(0, 0, M, 100, 10000);///体积最大是10000,所以高最高10000,半径最大100
if(min_Q < INF) printf("%d\n", min_Q);
else printf("0\n");
return 0;
}