[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\) 外,以上所有数据皆为正整数)

birthdaycake.png

思路

​ 简单的深搜,只需要记录每次的 \(H\)\(R\) ,依次深搜下去,在进行答案的比较,不过显然这会 \(TLE\)

​ 因此这也是一个练习剪枝很好的题,

  1. 当前的奶油面积+之后的最小奶油面积>现在已求出的的最小奶油面积——果断``return`;

  2. 当前的体积>n,return;

  3. 当前的体积+之后的最大体积<体积总数,果断return

  4. 发现每次枚举半径和高时,是从上一个的半径和高,到还剩下的层数。为什么呢,是因为每一层的半径和高都要比下一层的小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;
}
posted @ 2020-12-20 16:26  zxsoul  阅读(100)  评论(1编辑  收藏  举报