poj 1190 生日蛋糕
Total Submissions: 17460 Accepted: 6221
Description
7月17日是Mr.W的生日,ACM-THU为此要制作一个体积为 Nπ 的 M 层生日蛋糕,每层都是一个圆柱体。
设从下往上数第
由于要在蛋糕上抹奶油,为尽可能节约经费,我们希望蛋糕外表面(最下一层的下底面除外)的面积 Q 最小。
令
请编程对给出的
N
和
(除
Input
有两行,第一行为 N(N<=10000) ,表示待制作的蛋糕的体积为 Nπ ;第二行为 M(M<=20) ,表示蛋糕的层数为 M 。
Output
仅一行,是一个正整数
Sample Input
100
2
Sample Output
68
Hint
圆柱公式
体积
V=πR2H
侧面积
A′=2πRH
底面积
A=πR2
剪枝
剪枝1:搭建过程中发现已建好的面积已经超过目前求得的最优表面积,或者预见到搭完后面积一定会超过目前最优表面积,则停止搭建 (最优性剪枝)
剪枝2:搭建过程中预见到再往上搭,高度已经无法安排,或者半径已经无法安排,则停止搭建(可行性剪枝)
剪枝3:搭建过程中发现还没搭的那些层的体积,一定会超过还缺的体积,则停止搭建(可行性剪枝)
剪枝4:搭建过程中发现还没搭的那些层的体积,最大也到不了还缺的体积,则停止搭建(可行性剪枝)
#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAX_NUM = 20 + 5;
const int MAX_R = 100;
const int MAX_H = 100;
// M层,体积为N
int N, M;
// 最优化表面积
int minArea;
// 当前的表面积
int area;
// n层的最小体积
int minVolumn[MAX_NUM];
// n层的最小表面积
int minA[MAX_NUM];
void init() {
minVolumn[0] = 0;
minA[0] = 0;
for(int i = 1; i <= M; i++) {
// 第i层半径至少i,高度至少i
minVolumn[i] = minVolumn[i - 1] + i * i * i;
minA[i] = minA[i - 1] + 2 * i * i;
}
}
int maxVolumns[MAX_NUM][MAX_R][MAX_H];
// m层,底层最大半径为r,最高高度为h,能凑出的最大体积
int maxVolumn(int m, int r, int h) {
if(maxVolumns[m][r][h] >= 0) {
return maxVolumns[m][r][h];
}
int volumn = 0;
for(int i = 0; i < m; i++) {
volumn += (r - i) * (r - i) * (h - i);
}
maxVolumns[m][r][h] = volumn;
return maxVolumns[m][r][h];
}
// 使用r半径,h高,m层的蛋糕去凑出体积v
// 同时得到最小表面积
int dfs(int v, int m, int r, int h) {
// 递归出口
if(m == 0) {
// 剪枝,层数到了,但体积不为0
if(v){
return 1;
} else {
minArea = min(area, minArea);
return 0;
}
}
// 剪枝,体积已用完
if(v < 0) {
return 2;
}
// 剪枝,剩余m层所需要的体积要大于所拥有的总体积
if(minVolumn[m] > v) {
return 3;
}
// 剪枝,剩余m层的面积加上当前面积还大于等于当前遍历的所有状态的最小面积
if(minA[m] + area >= minArea) {
return 4;
}
// 剪枝,高度或者半径小于剩余层数
if(h < m || r < m) {
return 5;
}
// 剪枝,剩余最大的体积还是小于所需要的体积
if(maxVolumn(m, r, h) < v) {
return 6;
}
// 遍历递归
for(int i = r; i >= m; i--) {
// 底面积
if(m == M) {
area = i * i;
}
for(int j = h; j >= m; j--) {
// 侧面积
area += 2 * i * j;
int flag = dfs(v - i * i * j, m - 1, i - 1, j - 1);
// 回溯
area -= 2 * i * j;
// 剩余最大体积还是小于所需体积的话
// 那么高度更小,还是回小于所需体积的
if(flag == 6) {
break;
}
}
}
}
int main() {
cin >> N >> M;
// 初始化n层的最小面积和最小体积
init();
// 体积大于n层的最小体积,则无法成立
if(minVolumn[M] > N) {
cout << 0 << endl;
} else {
// 底层最大高度
int maxH = (N - minVolumn[M - 1]) / (M * M) + 1;
// 底层最大半径
int maxR = sqrt(double(N - minVolumn[M - 1]) / M) + 1;
// 初始化
area = 0;
minArea = 1 << 30;
memset(maxVolumns, -1, sizeof(maxVolumns));
dfs(N, M, maxR, maxH);
if(minArea == 1 << 30) {
cout << 0 << endl;
} else {
cout << minArea << endl;
}
}
return 0;
}