剪枝一 :
r[u] <= r[u + 1] - 1
//解释:因为蛋糕由低到高是逐渐递减的,所以上一层的蛋糕比下一层的蛋糕的半径小(最大是下一层的半径 - 1)
h[u] <= h[u + 1] - 1
同上
r[u] <= sqrt(n - v)
(这是一个假想最大情况) 因为我们可分配n - v的体积 , 如果将高度可以设为 1 。由 r * r * h = v 中转化过来。
优化:r[u] <= sqrt((n - v) / u) 因为我们高度每层最少为 u ,没必要从 1 开始。 (最小值)的情况 , 我们半径最大可以设为(sqrt) ((n - v) / u) 。
h[u] <= (n - v) / r / r 这个是在确定r之后 , 在剩余体积(n - v) 中最大可以分配 (n - v) / r / r 的高度 。由 r = (n - v) / h / h 转化过来。
剪枝二:
if s + mins[u] >= ans return ;
if v + minv[u] > n return ;
这两个剪枝很简单,我们当前摆放的体积若是加上当前能放的最小体积还大于要求体积。说明这个体积不合法(过大)
若是已经求出的体积加上我们能够尽可能少的最小侧面积比当前最优解还大,说明该分支一定走不到最优解
剪枝三:
从下向上搜
同剪枝一从大到小搜的想法一样 , 先枚举大的半径或高度 , 从小的分支开始搜 。 这是剪枝的经典搜索方式
剪枝四:
s + 2 * (n - v) / R[m + 1] >= ans
这个剪枝很奇怪,优化效果非常显著,我去掉这个就TLE了,这个剪枝的可以这样理解。
我们当前剩余分配的体积如果能以最大的半径去分配 , 形成的圆柱体的表面积加上是否会 大于最优解。
因为以可以分配的最大半径去形成圆柱的话 , 这个圆柱体形成的侧面积一定是最小的。有点类似于剪枝2的方式。不过也确实很难想。
则可以形成的最小高为(n - v) / R[m + 1] / R[m + 1]
(一定不可能半径大于下一层)
然后侧面积为 h * R[m + 1] * 2
加上 s得到上面的式子。
优化:
s + sqrt(n - v) * 2 >= ans
同理推出的剪枝
证明:
两个优化的代码:
#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 22 , Inf = 1e8;
int H[N] , R[N] ;
int n , m , ans = Inf;
int mins[N] , minv[N] ;
void dfs(int u , int s , int v)
{
if(s + mins[u] >= ans ) return ;
if(v + minv[u] > n ) return ;
if(s + 2 * sqrt(n - v) >= ans ) return ;
if(s + 2 * (n - v) / R[u + 1] >= ans ) return ;
if(!u)
{
if(v == n)
ans = s ;
return ;
}
for(int r = min(R[u + 1] - 1 , (int)sqrt((n - v) / u)) ; r >= u ; r -- )
for(int h = min(H[u + 1] - 1 , (n - v) / r / r ) ; h >= u ; h -- )
{
int t = 0 ;
if(u == m) t = r * r ;
R[u] = r ;
H[u] = h ;
dfs(u - 1 , s + t + 2 * r * h , v + r * h * r ) ;
}
}
int main(void)
{
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 ;
}
H[m + 1] = R[m + 1] = Inf;
dfs(m , 0 , 0) ;
if(ans != Inf)
cout << ans << endl ;
else
cout << "0" << endl ;
}
两个优化改后都Ac了,但是时间优化不明显,还是14ms左右~~~
哟,这不是月色嘛,字写得挺好看的
作者你证明那里不应该是 $s = 2 \times \frac{v}{r} $ 吗,是不是漏写了一个2
字好好看啊○| ̄|_
太$C$了
good
字写得好好看
假想的最大情况那里,大佬可以说清楚一点吗?没懂求教
假想的最大情况,是假设我们当前的h为1(最小时) , 我们所求的半径最长 r * r * h(h == 1) = n - v , 得出 r = sqrt(n - v)
明白,感谢