搜索:dfs + 剪枝 : 生日蛋糕

这篇博客介绍了如何解决一个关于制作多层圆柱形生日蛋糕的问题,其中目标是最小化蛋糕外表面的面积。作者分析了问题的关键点,包括搜索顺序剪枝、体积与面积的关系、可行性剪枝等,并提供了相应的代码实现。通过从底往上枚举半径和高度,结合优化的剪枝策略,找到了最小面积的蛋糕设计方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:https://www.acwing.com/problem/content/description/170/

题目:

7 月17 日是 Mr.W 的生日,ACM-THU 为此要制作一个体积为 Nπ 的 M 层生日蛋糕,每层都是一个圆柱体。

设从下往上数第 i 层蛋糕是半径为 Ri,高度为 Hi 的圆柱。

当 i<M时,要求 Ri>Ri+1 且 Hi>Hi+1。

由于要在蛋糕上抹奶油,为尽可能节约经费,我们希望蛋糕外表面(最下一层的下底面除外)的面积 Q 最小。

令 Q=Sπ ,请编程对给出的 N 和 M,找出蛋糕的制作方案(适当的 Ri 和 Hi 的值),使 S 最小。

除 Q 外,以上所有数据皆为正整数。

输入格式

输入包含两行,第一行为整数 N,表示待制作的蛋糕的体积为 Nπ。

第二行为整数 M,表示蛋糕的层数为 M。

输出格式

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

数据范围

1≤N≤10000,
1≤M≤20

输入样例:

100
2

输出样例:

68

分析:

 通过搜索的方式,我们枚举每一层的r 和 h ,求出其中表面积最小的情况。

1.优化搜索顺序剪枝: 为了让分支数尽量少,所以我们每次枚举都使用尽可能大的值。

而根据题意,下面一层的蛋糕的高 和 半径 都要严格大于上面一层的蛋糕。

所以我们从底往上枚举,同时先枚举R在枚举H。(因为体积用到的R为R^2,H只为H)

并且值从大到小进行枚举。

2.由于下面一层严格大于上面一层,所以从最高一层视为第一层的话,则第u层的半径r >= u , 高度h >= u . 同时 r < r[u + 1] , h < h[u + 1]

同时还存在一个限制条件。

由于我们DFS的时候,是从下往上的,所以r[u + 1],h[u + 1], 当前消耗的体积v都是已经求出来了的。

所以当前第u层: r^2 * h <= n - v.

h最小也要为1,则 r <= sqrt(n - v),   h <= (n - v) / r ^ 2

3. 初始化前n层所需要的最小值。 

由于dfs从底向上枚举,所以当前的s[u ~ end] + mins[1 ~ u - 1] < ans。 因为当前的s[u ~ end]已经确定,而最小满足情况下的mins最后的和都要 >= ans的话,则不用 在继续枚举了,直接return即可。

v[u ~ end] + minv[1 ~ u -1] <= n才行,同上面的,如果加上了最小值都大于 n 的话,就必定不满足,直接return即可。

4.

s[1 ~ u] = 2 * r[k] * h[k]   (k从1~u) 

v[1 ~ u] = n - v[u + 1 ~end] = r [k]* r[k] * h[k];

而这两个有一个什么样的关系呢?

s[1 ~ u] = 2 * r[k] * h[k]   (k从1~u)  = 2 / r[u + 1] *    r[k] * h[k] * r[u + 1]  (k从1~u)

>= 2 / r[u + 1] * r[k] * h[k] * r[k] >= 2 / r[u + 1]  * v[1 ~ u ] 

 其中为什么是大于等于呢?而不是大于呢?r[u + 1]不是必定大于r[k ]的嘛?

这是因为存在一个边界情况,就是当k 为最后一层的时候,

实际上就变为了 0 >= 0了。

所以为 >= 而不是 >

而这个有什么用呢?

s[1 ~ u] >= 2 / r[u + 1] * v[1 ~ u].

那么就向上面剪枝的时候需要考虑的一样,s[u + 1 ~ end] + s[1 ~ u] < ans才会继续向下枚举

也就是s(s的含义是s[u + 1 ~ end]) + 2 / r[u + 1] * v[1 ~ u] >= ans 直接return,不用向下枚举了。

代码实现:

# include <iostream>
# include <cmath>
using namespace std;

const int M = 25;

int minv[M];//minv[i], 1~i层的最小体积和 (高为1,R为1, 高为2,R为2....)
int mins[M];  // 其中这个mins考虑的是侧面积,不考虑顶层的面积,//mins[i],1 ~ i 层的最小面积和(高为1,R为1, 高为2,R为2....)

int n,m;

int ans = 0x3f3f3f3f;

int r[M],h[M];

void dfs(int u , int v , int s) // 当前已经弄完了 u + 1 到 m层,还剩下u层,现在已经用去了体积v,表面积已经达到了s
{
    if(u == 0) // 还剩下0层,也就是弄好了
    {
        if(v == n)
        {
            ans = min(s,ans);
        }
        return;
    }
    
    // 最优性剪枝
    if(s >= ans)
    {
        return;
    }
    
    // 可行性剪枝3
    if(s + mins[u] >= ans)
    {
        return;
    }
    if(v + minv[u] > n)
    {
        return;
    }
    
    // 可行性剪枝4
    if(s + 2 * (n - v) / r[u + 1] >= ans)
    {
        return;
    }
    
    
    // 可行性剪枝2 + 剪枝1  从大到小是优化搜索顺序
    for(int i = min(r[u + 1] - 1 , (int)sqrt(n - v)) ; i >= u ; i--)
    {
        for(int j = min(h[u + 1] - 1 , ( n - v) / i / i ) ; j >= u ; j--)
        {
            r[u] = i, h[u] = j;
            if(v + i * i * j > n)
            {
                continue;
            }
            if(u == m) // 当前为最下面那一层,加上 上表面积
            {
                dfs(u - 1, v + i * i * j , s + 2 * i * j + i * i);
            }
            else
            {
                dfs(u - 1, v + i * i * j , s + 2 * i * j );
            }
        }
    }
}

int main()
{
    scanf("%d %d",&n,&m);
    for(int i = 1 ; i <= m ; i++)
    {
        minv[i] = minv[i - 1] + i * i * i; // r^2 * h;
        mins[i] = mins[i - 1] + 2 * i * i;
    }
    
    r[m + 1] = 0x3f3f3f3f;
    h[m + 1] = 0x3f3f3f3f;
    
    dfs(m,0,0);
        
    if(ans == 0x3f3f3f3f)
    {
        ans = 0;
    }
    printf("%d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值