题目链接
思路
大体思路
记最底层为m,很容易观察得出,表面积的公式为
S总=Sm层上侧面积+∑i=1m2πRiHiS_总 = S_{m层上侧面积} + \sum_{i=1}^{m}2\pi R_iH_iS总?=Sm层上侧面积?+i=1∑m?2πRi?Hi?
而体积为
V总=∑i=1mπRi2HiV_总= \sum_{i=1}^{m}\pi R_i^2H_iV总?=i=1∑m?πRi2?Hi?
有了两个公式,还有题目给出的每层最小高度和最小半径,就知道可以用剪枝+暴搜来做这个题
图示
这个图对理解下面公式有帮助
剪枝优化
1.优化搜索顺序
- 层间:从下到上
- 层内:先枚举半径再枚举高(半径相对于高来说对体积的影响较大),半径由大到小,高度由大到小
2. 可行性剪枝
记总体积为n
,当前层位u
, 第u层的高度为HuH_uHu?, 半径为RuR_uRu?, 体积为VuV_uVu?, 第mmm层到第u层体积的累计值VVV
- 对于R, 当前为第
u
层, 第u
层的体积为VuV_uVu?。R最小的取值应该是当前的层号u ,R的最大值应该由两部分决定- u+1层的半径减1, 记Ru+1?1R_{u+1}-1Ru+1??1
- 第u层体积的最大值除第u层高度的最小值u
这两者的最小值, 故有以下等式成立
u≤Ru≤min{Ru+1?1,n?min∑i=1u?1Vi?Vu}u \leq R_u \leq min \lbrace R_{u+1}-1, \sqrt{\frac{n-min\sum_{i=1}^{u-1}V_i - V}{u}} \rbraceu≤Ru?≤min{
Ru+1??1,un?min∑i=1u?1?Vi??V??}
- 对于第u层高度h的推导同理,高度h的取值的最小值应该大于等于层号u,高度的最小值由两部分决定
- Hu+1?1H_{u+1}-1Hu+1??1
- 第u层体积的最大值除第u层的底面积最小值
故同理可得出下列等式
u≤Hu≤min{Hu+1?1,n?min∑i=1u?1Vi?VRu2}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} \rbraceu≤Hu?≤min{
Hu+1??1,Ru2?n?min∑i=1u?1?Vi??V?}
-
考虑体积的剪枝:预处理前u层的体积最小值min∑i=1u?1Vimin\sum_{i=1}^{u-1}V_imin∑i=1u?1?Vi?, 会有V+min∑i=1u?1Vi≤nV + min\sum_{i=1}^{u-1}V_i \leq nV+min∑i=1u?1?Vi?≤n
-
推表面积公式和体积公式的关系
第一层到第u层的表面积有(不考虑π\piπ)
S1?u=2∑i=1uRiHi=2Ru+1∑i=1uRu+1RiHi>2Ru+1∑i=1uRi2HiS_{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_iS1?u?=2i=1∑u?Ri?Hi?=Ru+1?2?i=1∑u?Ru+1?Ri?Hi?>Ru+1?2?i=1∑u?Ri2?Hi?
第一层到第u层的体积有
n?V=∑i=1uRi2Hin - V = \sum_{i=1}^{u}R_i^2H_in?V=i=1∑u?Ri2?Hi?
所以惊奇地发现
S1?u>2(n?V)Ru+1S_{1-u} >\frac{2(n-V)}{R_{u+1}}S1?u?>Ru+1?2(n?V)?
因此S总=S+S1?u>=SansS_总=S + S_{1-u}>=S_{ans}S总?=S+S1?u?>=Sans?,即S+2(n?V)Ru+1>=SansS + \frac{2(n-V)}{R_{u+1}} >= S_{ans}S+Ru+1?2(n?V)?>=Sans?时就可以剪枝掉(最优性剪枝)
3.最优性剪枝
记第mmm层到第u层表面积的累计值SSS, 第1到第u?1u-1u?1层表面积的最小值为 min∑i=1u?1Simin\sum_{i=1}^{u-1}S_imin∑i=1u?1?Si?
则应该有S+min∑i=1u?1Si<resS + min\sum_{i=1}^{u-1}S_i < resS+min∑i=1u?1?Si?<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*rint 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;
}