AcWing 168.生日蛋糕

题目链接

思路

大体思路

记最底层为m,很容易观察得出,表面积的公式为
S 总 = S m 层 上 侧 面 积 + ∑ i = 1 m 2 π R i H i S_总 = S_{m层上侧面积} + \sum_{i=1}^{m}2\pi R_iH_i S=Sm+i=1m2πRiHi
而体积为
V 总 = ∑ i = 1 m π R i 2 H i V_总= \sum_{i=1}^{m}\pi R_i^2H_i V=i=1mπRi2Hi

有了两个公式,还有题目给出的每层最小高度和最小半径,就知道可以用剪枝+暴搜来做这个题

图示

这个图对理解下面公式有帮助
在这里插入图片描述

剪枝优化

1.优化搜索顺序

  1. 层间:从下到上
  2. 层内:先枚举半径再枚举高(半径相对于高来说对体积的影响较大),半径由大到小,高度由大到小

2. 可行性剪枝

记总体积为n,当前层位u, 第u层的高度为 H u H_u Hu, 半径为 R u R_u Ru, 体积为 V u V_u Vu, 第 m m m层到第u层体积的累计值 V V V

  1. 对于R, 当前为第u层, 第u层的体积为 V u V_u Vu。R最小的取值应该是当前的层号u ,R的最大值应该由两部分决定
    • u+1层的半径减1, 记 R u + 1 − 1 R_{u+1}-1 Ru+11
    • 第u层体积的最大值除第u层高度的最小值u

这两者的最小值, 故有以下等式成立
u ≤ R u ≤ m i n { R u + 1 − 1 , n − m i n ∑ i = 1 u − 1 V i − V u } u \leq R_u \leq min \lbrace R_{u+1}-1, \sqrt{\frac{n-min\sum_{i=1}^{u-1}V_i - V}{u}} \rbrace uRumin{Ru+11,unmini=1u1ViV }

  1. 对于第u层高度h的推导同理,高度h的取值的最小值应该大于等于层号u,高度的最小值由两部分决定
    • H u + 1 − 1 H_{u+1}-1 Hu+11
    • 第u层体积的最大值除第u层的底面积最小值

故同理可得出下列等式
u ≤ H u ≤ m i n { H u + 1 − 1 , n − m i n ∑ i = 1 u − 1 V i − V R u 2 } u \leq H_u \leq min \lbrace H_{u+1}-1, \frac {{n-min\sum_{i=1}^{u-1}V_i - V}}{R_u^2} \rbrace uHumin{Hu+11,Ru2nmini=1u1ViV}

  1. 考虑体积的剪枝:预处理前u层的体积最小值 m i n ∑ i = 1 u − 1 V i min\sum_{i=1}^{u-1}V_i mini=1u1Vi, 会有 V + m i n ∑ i = 1 u − 1 V i ≤ n V + min\sum_{i=1}^{u-1}V_i \leq n V+mini=1u1Vin

  2. 推表面积公式和体积公式的关系

    第一层到第u层的表面积有(不考虑 π \pi π
    S 1 − u = 2 ∑ i = 1 u R i H i = 2 R u + 1 ∑ i = 1 u R u + 1 R i H i > 2 R u + 1 ∑ i = 1 u R i 2 H i S_{1-u} = 2\sum_{i=1}^{u}R_iH_i = \frac{2}{R_{u+1}} \sum_{i=1}^{u}R_{u+1}R_iH_i > \frac{2}{R_{u+1}}\sum_{i=1}^{u}R_i^2H_i S1u=2i=1uRiHi=Ru+12i=1uRu+1RiHi>Ru+12i=1uRi2Hi
    第一层到第u层的体积有
    n − V = ∑ i = 1 u R i 2 H i n - V = \sum_{i=1}^{u}R_i^2H_i nV=i=1uRi2Hi
    所以惊奇地发现
    S 1 − u > 2 ( n − V ) R u + 1 S_{1-u} >\frac{2(n-V)}{R_{u+1}} S1u>Ru+12(nV)
    因此如果有 S 1 − u ≤ 2 ( n − V ) R u + 1 S_{1-u} \leq \frac{2(n-V)}{R_{u+1}} S1uRu+12(nV)那么就可以剪枝掉

3.最优性剪枝

记第 m m m层到第u层表面积的累计值 S S S, 第1到第 u − 1 u-1 u1层表面积的最小值为 m i n ∑ i = 1 u − 1 S i min\sum_{i=1}^{u-1}S_i mini=1u1Si
则应该有 S + m i n ∑ i = 1 u − 1 S i < r e s S + min\sum_{i=1}^{u-1}S_i < res S+mini=1u1Si<res

4.排除等效冗余

至此,所有的剪枝都已经考虑清楚,码代码应该不是什么大问题

代码

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

const int N = 24, INF = 1e9;

int n, m;
int minv[N], mins[N];
int res = INF;

//记录每层的半径和高,因为会用到上一层的高度
int R[N], H[N];

//u当前层次,v当前处理的体积和,s当前处理的面积和
void dfs(int u, int v, int s)
{
    if(v + minv[u] > n) return;
    if(s + mins[u] >= res) return;
    if (s + 2 * (n - v) / R[u + 1] >= res) return;
    
    if(!u)
    {
        if(v == n) res = s;
        return;
    }
    
    //搜索顺序,先R后H,从大到小
    for(int r = min(R[u + 1] - 1,(int)sqrt((n - v - minv[u - 1]) / u)); r >= u; r--)
        for(int h = min(H[u + 1] - 1, (n - v - minv[u - 1]) / r / r); h >= u; h--)
        {
            H[u] = h, R[u] = r;
            
            //最底层的时候需要加上r*r
            int t = u == m ? r * r : 0;

            dfs(u - 1, v + r * r * h, s + 2 * r * h + t);
        }
}

int main()
{
    cin >> n >> m;
    for(int i = 1; i <= m; i++)
    {
        minv[i] = minv[i - 1] + i * i * i;
        mins[i] = mins[i - 1] + 2 * i * i;
    }
    
    //m+1层不存在,作为哨兵,减少边界情况的判断
    R[m + 1] = H[m + 1] = INF;
    
    dfs(m, 0, 0);
    
    if(res == INF) res = 0;
    cout << res << endl;
    

    return 0;
}
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 书香水墨 设计师:CSDN官方博客 返回首页