[NOI1999]生日蛋糕
生日蛋糕
题目描述
\(Mr.W\) 要制作一个体积为\(Nπ\) 的 \(M\) 层生日蛋糕,每层都是一个圆柱体。
设从下往上数第 \(i\) 蛋糕是半径为\(R_i\) ,高度为 \(H_i\)的圆柱。当\(i < M\) 时,要求\(R_i > R_{i+1}\) 且\(H_i > H_{i+1}\) 。由于要在蛋糕上抹奶油,为尽可能节约经费,我们希望蛋糕外表面(最下一层的下底面除外)的面积 \(Q\) 最小。
令 \(Q = Sπ\) ,请编程对给出的 \(N\) 和 \(M\) ,找出蛋糕的制作方案(适当的 \(R_i\)和 \(H_i\) 的值),使 \(S\) 最小。(除 \(Q\) 外,以上所有数据皆为正整数)
思路
简单的深搜,只需要记录每次的 \(H\) 和 \(R\) ,依次深搜下去,在进行答案的比较,不过显然这会 \(TLE\)
因此这也是一个练习剪枝很好的题,
-
当前的奶油面积+之后的最小奶油面积>现在已求出的的最小奶油面积——果断``return`;
-
当前的体积>n,
return
; -
当前的体积+之后的最大体积<体积总数,果断
return
; -
发现每次枚举半径和高时,是从上一个的半径和高,到还剩下的层数。为什么呢,是因为每一层的半径和高都要比下一层的小1,所以你得每一层都留一个1,\(So\),是从上一个的半径和高,到还剩下的层数;
Code
#include <bits/stdc++.h>
using namespace std;
int n, m;
const int manx = 1e6;
int ans = 987654321;
int mins[manx],minv[manx];
inline void dfs(int c,int v, int s,int h,int r) {//层数,半径,高度,体积,抹奶油的外表面积
if(c == 0){
if(v == n) ans = min(ans ,s);
return;
}
if(v + minv[c] > n) return;
if(s + mins[c] > ans) return;
if(s + 2 *(n-v) / r > ans ) return;
for (int i = r - 1; i >= c; --i)
{
if(c == m) s = i * i;
int Maxh = min(h - 1,(n - v - minv[c - 1])/(i * i));
for (int j = Maxh; j >= c; --j)
dfs(c - 1, v + i * i * j, s + 2 * i * j,j,i);
}}
int main() {
scanf("%d%d", &n, &m);
int MaxR = sqrt(n);
for(int i = 1;i <= n; i++){
minv[i] = minv[i-1] + i * i * i;
mins[i] = mins[i-1] + 2 * i * i;
}
// for (int i = m; i <= nn; ++i)//因为有m层,所以第一层至少为m,至多推公式
// for (int j = m; j <= n / (m * m); ++j)//范围也是由体积公式推理而得
// dfs(1, i, j, n, i * i );
dfs(m,0,0,n,MaxR);
if(ans == 987654321)
cout<<0<<endl;
else cout<<ans<<endl;
return 0;
}