题目链接:poj1190 生日蛋糕
解题思路:
深搜,枚举:每一层可能的高度和半径
确定搜索范围:底层蛋糕的最大可能半径和最大可能高度
搜索顺序:从底层往上搭蛋糕,在同一层尝试时,半径和高度都是从大到小试
剪枝:
①已建好的面积已经超过目前求得的最优表面积,或者预见到搭完后面积一定会超过目前最优表面积,则停止搭建(最优性剪枝)
②预见到再往上搭,高度已经无法安排,或者半径无法安排,则停止搭建(可行性剪枝)
③还没搭的那些层的体积,一定会超过还缺的体积,则停止搭建(可行性剪枝)
④还没搭的那些层的体积,最大也到不了还缺的体积,则停止搭建(可行性剪枝)
看讨论时看见了一个神剪枝:当2 * leftVolume / r + currentS >= min停止搜索
currentS代表“已有的圆柱的侧面积之和+最底下圆柱的横截面面积”。min代表已得到的最小表面积。
假设只有一个圆柱,该圆柱的半径为r,体积为leftVolume,可知:2 * leftVolume/r 表示圆柱的侧面积。
现在我们有2个圆柱,要求这两个圆柱叠在一起之后满足题目的条件:下柱半径>上柱半径。把上柱压扁,压到和下柱的半径相等,那么根据表面积和体积公式,我们知道上柱的侧面积会减小。
多个圆柱叠立,假设最下面圆柱半径最大,该半径为r。于是,这些圆柱的侧面积之和>=等体积的半径为r的圆柱的侧面积。
假设还有k层柱要搜索,leftVolume是剩余体积,r是第k层的圆柱的最大可能半径。那么2*leftVolume/r<=k层圆柱的最小侧面积之和。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define CLR(a,b) memset((a),(b),sizeof(a))
using namespace std; const int inf = 0x3f3f3f3f;
int ans; //最优表面积
int area; //正在搭建中的蛋糕的表面积
int N, M;//体积N,层数M
int minv[]; //第i层蛋糕最少的体积
int mins[]; //第i层蛋糕的最少侧表面积 int maxV(int n, int r, int h){
//在n层蛋糕,底层最大半径r,最高高度h的情况下,能凑出来的最大体积
int v = ;
for(int i = ; i < n; ++i)
v += (r - i) * (r - i) * (h - i);
return v;
}
void dfs(int v, int n, int r, int h){
//用n层去凑体积v,最底层半径最大为r, 高度最大为 h
//求出最小表面积放入ans
if(n == ){
if(v == && area < ans){
ans = area;
return;
}
}
if(v <= ) return; if((*v*./r+area)>=ans) return;
if(area + mins[n] >= ans ) return;
if(h < n || r < n) return;
if(v < minv[n]) return;
if(maxV(n, r, h) < v) return;
for(int rr = r ; rr >= n; --rr){//从大到小搜索!
if(n == M) //底面积(总的上表面积)
area = rr * rr;
for(int hh = n; hh <= h; ++hh){
area += * rr * hh;
dfs(v - rr*rr*hh, n-, rr-, hh-);
area -= * rr * hh;
}
}
}
int main(){
int i, j;
int maxh;//底层最大高度
int maxr;//底层最大半径
scanf("%d %d", &N, &M); CLR(minv, ); CLR(mins, ); for(i = ; i <= M; ++i){
//第i层半径至少为i,,高度至少为i
minv[i] = minv[i - ] + i * i * i;
mins[i] = mins[i - ] + * i * i;
}
if(minv[M] > N){
printf("0\n"); return ;
}
area = ;
ans = inf;
//底层体积不超过(n - minv[m - 1])
maxh = (N - minv[M - ]) / (M * M) + ; //底层半径至少为 m
maxr = sqrt(.*(N - minv[M-]) / M) + ;//底层高度至少为 m dfs(N, M, maxr, maxh); if(ans == inf) printf("0\n");
else printf("%d\n", ans);
return ;
}