生日蛋糕
7月17日是Mr.W的生日,ACM-THU为此要制作一个体积为 Nπ 的 M 层生日蛋糕,每层都是一个圆柱体。
设从下往上数第i层蛋糕是半径为Ri, 高度为 Hi 的圆柱。
当 i < M 时,要求Ri > Ri+1且Hi > Hi+1。
由于要在蛋糕上抹奶油,为尽可能节约经费,我们希望蛋糕外表面(最下一层的下底面除外)的面积Q最小。
令Q = Sπ ,请编程对给出的N和M,找出蛋糕的制作方案(适当的Ri和Hi的值),使S最小。
除Q外,以上所有数据皆为正整数 。
输入格式
输入包含两行,第一行为整数N(N <= 10000),表示待制作的蛋糕的体积为NπNπ。
第二行为整数M(M <= 20),表示蛋糕的层数为M。
输出格式
输出仅一行,是一个正整数S(若无解则S = 0)。
数据范围
1≤N≤10000
1≤M≤20
题意:
体积固定为 n π 、固定m层,每一层都是一个圆柱体,
从上往下,圆柱的半径、高度二者必须都是递减的,
也就是 r[i+1]>r[i] 、 h[i+1]>h[i]
找出一个方案使得整个圆柱的外表面积最小(除了最底下那一层的下低面)
最后的答案除π
题解:
搜索包含的状态:当前的层数,已经确定的体积,已经确定的面积
题目中对最后的答案除圆周率,所以可以直接忽略
1)优化搜索顺序
从底向上枚举
2)上下界剪枝
先枚举 r,再枚举 h
R ∈ [ deep , min( |sqrt(n-v)|, r[deep+1]-1 ) ]
H ∈[ deep , min( (n - v) / R^2 , h[deep +1) -1 ]
3)可行性剪枝
预处理从上往下,第i层最小的体积和侧面积如果当前的体积加上前i层的最小体积都 > N 直接回溯
4)最优性剪枝
如果当前层 i 的表面积加上前面已经得到的最小侧面积大于搜索到的答案可以直接剪枝
当
2*(n-v)/R[u+1]+s 的表面积大于已搜到的答案的时候剪枝
以上两个剪枝保证了搜到底符合条件的答案一定是可以去更新当前答案的所以一定是最优解
代码:
- #include<bits/stdc++.h>
- using namespace std;
- const int N=25;
- int n,m;
- int R[N],H[N];
- int minv[N],mins[N];
- int ans=INT_MAX;
- void dfs(int u,int v,int s){
- if(v+minv[u] > n) return;
- if(s+mins[u] >= ans) return;
- if(2*(n-v)/R[u+1]+s>=ans) return;//关键最优性剪枝
- if(!u){
- if(v==n) ans=s;//剪枝将所有大的都剪掉了剩下的一定是能够更新最小值的
- return;
- }
- for(int r = min( R[u+1]-1,(int)sqrt(n-v)); r >= u; r --)//优化搜索顺序,先从大的r开始,h由r确定最大
- for(int h = min( H[u+1] - 1, (n - v) / r / r); h >= u; h--){
- int t=0;
- if(u==m)
- t=r*r;
- R[u]=r;
- H[u]=h;
- dfs(u-1,v+r*r*h,s+t+2*r*h);
- }
- }
- int main(){
- cin>>n>>m;
- for(int i=1;i<=m;i++){
- minv[i]=i*i*i;
- mins[i]=2*i*i;
- }
- R[m+1]=H[m+1]=INT_MAX;
- dfs(m,0,0);
- cout<<ans<<endl;
- }