题目来源:http://noi.openjudge.cn/ch0407/192/
192:生日蛋糕
总时间限制: 5000ms 内存限制: 65536kB
描述
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= πR2H
侧面积A' = 2πRH
底面积A = πR2
来源
Noi 99
-----------------------------------------------------
思路
深搜+剪枝。这题的搜索和剪枝都特别有技巧性,剪了一个下午,还是看了别人博客上的代码才能不超时的。
搜索从底层最大的那层开始往上搜,要先搜半径再搜高度,高度和半径都要从大往小搜,这样有利于剪枝。
剪枝条件有3个。2个最优剪枝,1个可行性剪枝。
最优剪枝1:如当前表面积已经大于最小表面积,剪枝(这个很好想)
最优剪枝2:如果当前表面积加剩余体积对应的那部分表面积的下界大于最小表面积,剪枝。由于每层的体积贡献是 ,除了最下面那层表面积贡献是 ,故剩余体积 对应的表面积下界可以简单用 估计。(这个挺难想到的,但这步剪枝是从TLE到AC的关键)
可行性剪枝:如果当前的体积已经大于n,剪枝(这个也很好想)
-----------------------------------------------------
代码
// 深搜+剪枝
#include<iostream>
#include<climits>
#include<cmath>
using namespace std;
int n,m; // n: 体积,m: 层数
int optimal = INT_MAX; // 最小表面积
int minv[25] = {}; // 最顶上i层体积的下确界
void dfs(int d, int area, int vol, int preH, int preR) // d: 搜索层深, area: 当前的表面积, vol: 当前的体积, preH: 上一层的高, preR: 上一层的半径
{
int i,j,area1,vol1;
if (d==m)
{
if (vol==n)
{
optimal = min(area, optimal); // 更新最小表面积
}
return;
}
if (vol > n) // 体积超出n,剪枝
{
return;
}
if (area > optimal) // 当前面积已经超过最小面积,剪枝
{
return;
}
if (vol + minv[m-d-1] > n) // 如果当前体积的顶上放最小体积还是会超体积,剪枝
{
return;
}
if (area + 2*(n-vol)/preR >= optimal)// 如果当前表面积的顶上放最小表面积还是大于等于最小表面积,剪枝(顶上最小面积<2*剩余的体积/上次的半径)
{
return;
}
if (d==0)
{
for (j=((int) sqrt(n)); j>=m; j--) // 先枚举半径
{
for (i=n/j/j; i>=m; i--) // 再枚举高度
{
area1 = 2*i*j + j*j; // 第0层决定上表面积
vol1 = i*j*j;
dfs(1,area1,vol1,i,j);
}
}
}
else
{
for (j=min(((int) sqrt(n-vol)),preR-1); j>=m-d; j--)
{
for (i=min((n-vol)/j/j,preH-1); i>=m-d; i--)
{
area1 = area + 2*i*j;
vol1 = vol + i*j*j;
dfs(d+1, area1, vol1, i, j);
}
}
}
}
int main()
{
int i;
cin >> n >> m;
minv[0] = 1*1*1;
for (i=1; i<m; i++)
{
minv[i] = minv[i-1] + (i+1)*(i+1)*(i+1);
}
dfs(0,0,0,n+1,((int) sqrt(n))+1);
cout << ((optimal<INT_MAX)? optimal:0);
return 0;
}