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
题解
搜索的
奇技淫巧系列
首先表面积一定包含了最底下那个圆柱的圆面积,我们只需要计算侧面积再加上那个圆面积就好
从底往上搜,设最底端圆柱编号为1且编号向上递增,然后开始一通瞎剪
设当前枚举到编号为k的圆柱,下面的体积和为v,侧面积和为s
用r[i],h[i]记录编号为i的圆柱半径及高度
1:对于搜索当前答案>=最小答案直接剪掉这不是废话吗
2:枚举半径的时候从大往小枚举为什么后面再说
3:对于半径的枚举,我们限制范围为[k,min(sqrt(n−v)),r[k−1]−1][k,min(sqrt(n−v)),r[k−1]−1]
4:对于高度的枚举,我们限制范围为[k,min((n−v)/R2,h[k−1]−1)][k,min((n−v)/R2,h[k−1]−1)],其中R是枚举的半径
5:推一个柿子
对于第i个圆柱,他和他上面的圆柱体积和一定要为n-v,可替代为sigma(r[i]2∗h[i])sigma(r[i]2∗h[i])
同理,表面积和即为2∗sigma(r[i]∗h[i])2∗sigma(r[i]∗h[i])
处理一下表面积的柿子,可以得到2/r[k]∗sigma(r[i]∗r[k]∗h[i])2/r[k]∗sigma(r[i]∗r[k]∗h[i])
再处理第一个柿子,可以得到2/r[k]∗sigma(r[i]2∗h[i])=(n−v)∗2/r[k]2/r[k]∗sigma(r[i]2∗h[i])=(n−v)∗2/r[k]
由于r[k]一定大于任意一个r[i],所以有
2/r[k]∗sigma(r[i]∗r[k]∗h[i])>=(n−v)∗2/r[k]2/r[k]∗sigma(r[i]∗r[k]∗h[i])>=(n−v)∗2/r[k]
由于第一式代表的实际为第i个圆柱及其上面的圆柱的侧面积和,所以他+s一定要小于最小答案,但是他比较难算出所以不考虑这里剪枝。由于后面那个式子可以快速算出,且他不会大于第一式。那么在满足(n−v)∗2/r[k]+s>=ans(n−v)∗2/r[k]+s>=ans时,第一式也一定满足,于是可以通过这个剪枝
然后你就可以发现,为什么要倒着枚举了
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
int n,m,ans;
int h[11000],r[11000];
int mncos[11000];
void dfs(int k,int s,int v)
{
if(k==m+1)
{
if(v==n)ans=min(ans,s);
return ;
}
int u=min(int(double(sqrt(n-v))),r[k-1]-1);
for(int i=u;i>=(m-k+1);i--)
{
if(2*(n-v)/i+s>=ans)continue;
for(int j=(m-k+1);j<=min((n-v)/(i*i),h[k-1]-1);j++)
{
if(s+2*i*j+mncos[k+1]>=ans)break;
if(v+i*i*j>n)break;
r[k]=i;h[k]=j;
if(k!=1)dfs(k+1,s+2*i*j,v+i*i*j);
else dfs(k+1,s+2*i*j+i*i,v+i*i*j);
r[k]=h[k]=0;
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=m;i>=1;i--)mncos[i]=mncos[i+1]+2*(m-i+1)*(m-i+1);
ans=999999999;r[0]=h[0]=999999999;
dfs(1,0,0);
if(ans==999999999)printf("0\n");
else printf("%d\n",ans);
return 0;
}