题目链接
思路
大体思路
记最底层为m,很容易观察得出,表面积的公式为
$$S_总 = S_{m层上侧面积} + \sum_{i=1}^{m}2\pi R_iH_i$$
而体积为
$$V_总= \sum_{i=1}^{m}\pi R_i^2H_i$$
有了两个公式,还有题目给出的每层最小高度和最小半径,就知道可以用剪枝+暴搜来做这个题
图示
这个图对理解下面公式有帮助
剪枝优化
1.优化搜索顺序
- 层间:从下到上
- 层内:先枚举半径再枚举高(半径相对于高来说对体积的影响较大),半径由大到小,高度由大到小
2. 可行性剪枝
记总体积为n
,当前层位u
, 第u层的高度为$H_u$, 半径为$R_u$, 体积为$V_u$, 第$m$层到第u层体积的累计值$V$
- 对于R, 当前为第
u
层, 第u
层的体积为$V_u$。R最小的取值应该是当前的层号u ,R的最大值应该由两部分决定- u+1层的半径减1, 记$R_{u+1}-1$
- 第u层体积的最大值除第u层高度的最小值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$$
- 对于第u层高度h的推导同理,高度h的取值的最小值应该大于等于层号u,高度的最小值由两部分决定
- $H_{u+1}-1$
- 第u层体积的最大值除第u层的底面积最小值
故同理可得出下列等式
$$ 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$$
-
考虑体积的剪枝:预处理前u层的体积最小值$min\sum_{i=1}^{u-1}V_i$, 会有$V + min\sum_{i=1}^{u-1}V_i \leq n$
-
推表面积公式和体积公式的关系
第一层到第u层的表面积有(不考虑$\pi$)
$$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$$
第一层到第u层的体积有
$$n - V = \sum_{i=1}^{u}R_i^2H_i$$
所以惊奇地发现
$$S_{1-u} >\frac{2(n-V)}{R_{u+1}}$$
因此$S_总=S + S_{1-u}>=S_{ans}$,即$S + \frac{2(n-V)}{R_{u+1}} >= S_{ans}$时就可以剪枝掉(最优性剪枝)3.最优性剪枝
记第$m$层到第u层表面积的累计值$S$, 第1到第$u-1$层表面积的最小值为 $min\sum_{i=1}^{u-1}S_i$
则应该有$S + min\sum_{i=1}^{u-1}S_i < 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;
}
楼主给出了比书中更紧的R_u和H_u上界,精彩!
markdown炸了。。。
你看第一个等式。。。。
问下为啥表面积是那个公式啊
那个侧面积不就是后面的等式吗?
m层时,表面积看成是两部分:侧面积和底面积,也就是说,把上面所有层的底面积统一看成是第m层的了
之所以可以这样来看,是因为:
##### 上表面积$S_上$
第$m$层的上表面积是$S_m - S_{m-1}$
第$m-1$层的上表面积是$S_{m-1} - S_{m-2}$
…
第$2$层的表面积是$S_2-S_1$
* 第$1$层的上表面积是$S_1$
$$\large \therefore S_{上}=S_m-S_{m-1}+S_{m-1}-S_{m-2}+…+S_2-S_1+S_1=S_m=\pi R_m^2$$
现在懂了,谢谢DALAO。
tql
V应该表示的是第m层到第u-1层的累计体积吧,是不写错了
我也感觉这样
是$m \sim u+1$
如果做题看题解都会了,自己想的话感觉没有思考的方向该怎么办啊
newbility
请问这个为什么不能回溯呢
楼主给出了比书中更紧的R_u和H_u上界,精彩!
orz
没看懂0 0为啥R和U的那个上限要多减一个min∑u−1i=1Vi呢,,n-u不就是剩下的体积么,,求解
n - v是包括u这一行剩余的体积,然后再减去minv[u - 1]是u这一行最大的体积,所以(n - v - minv[u - 1])是u这一行最大体积,然后除以u,后面除的u是指最小的高度。然后这个式子就求出来最大的半径
orz, 讲的非常清楚!
Orz
有一处笔误,可行性剪枝的第二个剪枝:
高度的最小值
,应该是最大值才对
怎么画的图捏
楼主给出了比y总更紧的R_u和H_u上界,精彩!
Sixsixsix
tql
Orz
for(int r = min(R[u + 1] - 1,(int)sqrt((n - v - minv[u - 1]) / u)这里为啥取min 呢?除了第一项其他的不都是取max吗?一开始r越大越好啊
两个都是r的上界需要取最小上界,避免无意义的搜索,也可以看作是一种剪枝
orz
orz