POJ百炼——1190生日蛋糕

心已入冬 提交于 2020-02-10 21:53:28

题目如下:

1190:生日蛋糕

总时间限制:
5000ms
内存限制:
65536kB
描述
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外,以上所有数据皆为正整数)
输入
有两行,第一行为N(N <= 10000),表示待制作的蛋糕的体积为Nπ;第二行为M(M <= 20),表示蛋糕的层数为M。
输出
仅一行,是一个正整数S(若无解则S = 0)。
样例输入
100
2
样例输出
             68
        这一题的主要思路是搜索与剪枝。我也是在借鉴了其他大佬的思路后才做出来的。
        首先,通过分析题目中的“除Q外,所有数据均为整数”,可以得出,我们可以以1为步长进行枚举。当体积最大及N为10000时,半径r最大为100(当高度取最小值1时),高度h最大值为10000(当半径取最小值1时)。这样就确定对半径r和高度h枚举的上界了。至于下界,我们设蛋糕从顶至底分别是第1层至第M层(注意,与题目中规定的层的顺序相反。后面会发现这样相反的顺序更加方便),第i层的最小半径是i,因为从第i层向上到第1层,每层半径至少减少1,故得出这样的结果。同理,第i层最小高度为i。
        有了代码搜索的上下界,就可以进行搜索。搜索过程需要剪枝。剪枝的方法有很多。第一种,当蛋糕摆到第i层时,剩下的层摆放所需的最小面积加上已经摆放的蛋糕的面积已经大于了当前最佳的面积;或者剩下层蛋糕所需的最小体积加上已摆放的体积大于了总体积。这种情况可以直接跳过,进入下一个搜索。第二种,利用数学推论,当摆到第I层时,剩下的体积摆成一个圆柱体的情况是表面积最好的情况。如果在这种条件下表面积之和仍小于当前最佳表面积,则可以直接跳过。这种方法我看的大佬的,具体如下:
 /**
     *  2*r*h=S
     *  r*r*h=V
     *  => V*2/r = S
     *  S + sumS >= best => return;
     */

      这两种剪枝方式可以同时使用。下面给出代码,代码是借鉴了大神的代码之后写出来的:

#include<iostream>
#include<map>
#include<string>
#include<cmath>
#include<iomanip>
#include<algorithm>
#include<memory.h>
using namespace std;
int N, M;
int best;//记录最小的表面积
int mins[20];//第i层时剩下层数最小的表面积
int minv[20];//第i层时剩下层数最小的体积
void solve(int m, int sums, int sumv, int r, int h)//m表示当前从上至下是第几层,sums代表当前表面积,sumv代表当前所用体积,r代表当前半径,h代表当前高度。
{
    if (m == 0)//最下面一层摆好后,判断是否更新最小表面积
    {
        if (best > sums&&sumv==N)
        {
            best = sums;
        }
        return;
    }
    if ((mins[m] + sums) > best || (minv[m] + sumv) > N)//第一种剪枝
    {
        return;
    }
    if ((2 * (N - sumv) / r + sums) >= best)//第二种剪枝
    {
        return;
    }
    for (int i = r - 1; i >= m; i--)//进行搜索
    {
        if (m == M)//对于第一层的摆放,计算上底面积
        {
            sums = i * i;
        }
        for (int j = h - 1; j >= m; j--)
        {
            solve(m - 1, sums + i * j * 2, sumv + i * i*j, i, j);//搜索递归的进行
        }
    }
}
int main()
{
    cin >> N >> M;
    for (int i = 1; i < 20; i++)//计算相应的值
    {
        mins[i] = mins[i - 1] + i * i * 2;
        minv[i] = minv[i - 1] + i * i * i;
    }
    best = 9999999;//best的初始值要大一些
    solve(M, 0, 0, 100, 10000);
    if (best == 9999999)
    {
        best = 0;
    }
    cout << best << endl;
    return 0;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!