poj 1190 生日蛋糕

poj 1190 生日蛋糕

Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 17460 Accepted: 6221

Description

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

Input

有两行,第一行为 NN<=10000 ,表示待制作的蛋糕的体积为 Nπ ;第二行为 M(M<=20) ,表示蛋糕的层数为 M

Output

仅一行,是一个正整数S(若无解则 S=0 )。

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;
 }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值