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
极高,深搜,动态规划
首先,整个过程不需要考虑Pi
圆柱体积 V = r * r * h
圆柱侧面积 S = 2 * r * h
圆柱底面积 C = r * r
蛋糕的层数N (N <= 20) ,蛋糕体积V(<=100000)
可以用递推公式算出第i层到第N层蛋糕圆柱侧面积的最小值之和
可以用递推公式算出第i层到第N层蛋糕圆柱体积的最小值之和
根据以上的计算结果,可以估算出底面积半径的最大值MaxR
根据以上的计算结果,可以估算出最底层的最大高度值MaxH
最底层圆柱的区间 H = 【1 ,maxH】
最底层圆柱的底半径 N =【N,maxR】
由于要计算表面积(露出来的),那么在一层一层的计算过程中,如果最下面的一层确定了,则上表面积就确定了,所以搜索框架从下到上。
深度优先搜索的搜索目标:枚举每一层可能的高度和半径,找到可行方案,记录圆柱体的表面积之和
搜索面对的状态有:第几层,半径和高度,剩余体积,累计表面积
搜索和枚举的顺序? 从大开始递减枚举 从底层往上搭,从N开始递归搜索
启发式剪枝:
预估此路径剩余体积的最小表面积值,若超过全局变量的最小值,则无需继续搜索
可行性剪枝:
剩余体积小于ifloor的最小体积,体积余量不足,无需继续搜索
可行性剪枝+启发性剪枝 :
累计表面积+最小表面积>全局最小表面积,无需继续搜索
//code by Andy
#include
using namespace std;
#define INF 0x7fffffff
#define ButtonArea(r) (r*r) //底面积
#define surArea(r,h) (2*r*h) //侧面积
#define Volume(r,h) (r*r*h) //体积
#define V2surArea(r,v) (2*v/r) //根据体积和半径计算侧面积
int V,N,minsurArea = INF;
//递推计算各层的体积与侧面积区间下界
int sumMinS[27],sumMinV[27];
//第几层、半径、高度、剩余体积、累计表面积
void dfs0(int ifloor,int preR,int preH,int leftV,int surArea)
{
if(ifloor==0){
//唯有剩余体积刚好为0,此路径有解,若累计面积较小就记录全局变量
if(leftV==0 && surArea<minsurArea)
minsurArea = surArea;
return;
}
//启发式剪枝,预估此路径剩余体积的最小表面积值,若超过全局变量的最小值,则无需继续搜索
if(preR>1 && V2surArea(preR-1,leftV)+surArea >=minsurArea) return;
//可行性剪枝
if(leftV<sumMinV[ifloor]) return;
//可行性剪枝+启发性剪枝 累计表面积+最小表面积>全局最小表面积
if(surArea+sumMinS[ifloor]>=minsurArea) return;
//枚举所有的R和H,深搜
for(int r=preR-1;r>=ifloor;r--){
if(ifloor==N) surArea = ButtonArea(r); //最底层面积
//确定高度枚举范围上界
int H_max = 1.0*leftV/ButtonArea(r)+1; //剩余体积除于底面积为高
if(H_max>preH-1) H_max=preH-1; //高的最大值
for(int h=H_max;h>=ifloor;h--)
dfs0(ifloor-1,r,h,leftV-Volume(r,h),surArea+surArea(r,h));
}
}
int main() {
cin>>V>>N;
//从顶层往下递推计算,最高层为0
sumMinS[0]=sumMinV[0]=0;
for(int i=1;i<=N;i++){
//所有半径和高度都是正整数,所以可得下面递推式
sumMinS[i] = sumMinS[i-1] + surArea(i,i);
sumMinV[i] = sumMinV[i-1] + Volume(i,i);
}
//最底层最大的H和R
//最顶层体积最小,最底层半径最大为N
int maxH = (V-sumMinV[N-1])/ButtonArea(N)+1;
//高度为1时圆柱半径最大
int maxR = sqrt(double((V-sumMinV[N-1])+1));
minsurArea = INF; //将minsubArea置成一个很大的值
dfs0(N,maxR,maxH,V,0);
if(minsurArea==INF)
cout<<0<<endl;
else
cout<<minsurArea<<endl;
return 0;
}
需要找到一种方案从头至尾的最优解需要使用深搜,与动态规划,贪心算法不同的是,采用深度优先搜索求最优解的问题不具有最优子性质。而在深搜的过程中,复杂度较高,需要进行适当的剪枝降低时间复杂度。主要的剪枝方案有可行性剪枝和启发性剪枝。