题目链接
思路
大体思路
记最底层为m,很容易观察得出,表面积的公式为
S总=Sm层上侧面积+m∑i=12πRiHi
而体积为
V总=m∑i=1πR2iHi
有了两个公式,还有题目给出的每层最小高度和最小半径,就知道可以用剪枝+暴搜来做这个题
图示
这个图对理解下面公式有帮助
剪枝优化
1.优化搜索顺序
- 层间:从下到上
- 层内:先枚举半径再枚举高(半径相对于高来说对体积的影响较大),半径由大到小,高度由大到小
2. 可行性剪枝
记总体积为n
,当前层位u
, 第u层的高度为Hu, 半径为Ru, 体积为Vu, 第m层到第u层体积的累计值V
- 对于R, 当前为第
u
层, 第u
层的体积为Vu。R最小的取值应该是当前的层号u ,R的最大值应该由两部分决定- u+1层的半径减1, 记Ru+1−1
- 第u层体积的最大值除第u层高度的最小值u
这两者的最小值, 故有以下等式成立
u≤Ru≤min{Ru+1−1,√n−min∑u−1i=1Vi−Vu}
- 对于第u层高度h的推导同理,高度h的取值的最小值应该大于等于层号u,高度的最小值由两部分决定
- Hu+1−1
- 第u层体积的最大值除第u层的底面积最小值
故同理可得出下列等式
u≤Hu≤min{Hu+1−1,n−min∑u−1i=1Vi−VR2u}
-
考虑体积的剪枝:预处理前u层的体积最小值min∑u−1i=1Vi, 会有V+min∑u−1i=1Vi≤n
-
推表面积公式和体积公式的关系
第一层到第u层的表面积有(不考虑π)
S1−u=2u∑i=1RiHi=2Ru+1u∑i=1Ru+1RiHi>2Ru+1u∑i=1R2iHi
第一层到第u层的体积有
n−V=u∑i=1R2iHi
所以惊奇地发现
S1−u>2(n−V)Ru+1
因此S总=S+S1−u>=Sans,即S+2(n−V)Ru+1>=Sans时就可以剪枝掉(最优性剪枝)3.最优性剪枝
记第m层到第u层表面积的累计值S, 第1到第u−1层表面积的最小值为 min∑u−1i=1Si
则应该有S+min∑u−1i=1Si<res4.排除等效冗余至此,所有的剪枝都已经考虑清楚,码代码应该不是什么大问题代码
#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层的上表面积是Sm−Sm−1
第m−1层的上表面积是Sm−1−Sm−2
…
第2层的表面积是S2−S1
* 第1层的上表面积是S1
∴
现在懂了,谢谢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