P1731. 生日蛋糕
考点
- DFS
- 剪枝
题解
1 |
|
思路
上面题解是拿加强版做的,本题数据很烂
根据题意,第1层是最小的,第m层是最大的
从小到大,单独每个圆柱体的上表面积等于s1、s2、s3;
按照题意累堆在一起,那么总上表面积等于s1 + (s2 - s1) + (s3 - s2) = s3
所以处理第m层时,就可以直接加上总上表面积,只需要考虑每个部分的侧面积处理
根据题意,r[k]代表第k层的半径,h[k]代表第k层的高度,那么有:
r[0] = h[0] = 0, r[m + 1] = h[m + 1] = infr[1] < r[2] < r[3] < ... < r[m]h[1] < h[2] < h[3] < ... < h[m]r[k] >= k, h[k] >= k- 第
k层的最小侧面积等于2 * k * k,第k层的最小体积等于k * k * k
可以用mins[k]、minv[k]预先打表1 ~ k层的最小侧面积、最小体积的前缀和
接下来到各种恶心的剪枝环节:
假设当前层数为now,now + 1 ~ m的体积和为v,now + 1 ~ m的侧面积和为s
搜索顺序
从第m层倒序搜索至第1层,r和h也从大到小遍历,必须记住的剪枝技巧!
逻辑上理解,先固定大的,小的可能性搜索树自然就会少。
r和h的取值范围
根据公式: \[
r^2h=\left( n-v \right) \Longrightarrow r=\sqrt{\frac{\left( n-v
\right)}{h}}
\]
而第now层的h最小值为now,那么r的最大值为:
1 | sqrt(((double)n - v) / now) |
注意!除法操作时先转换成double,避免精度缺失导致WA
又因为当前的半径肯定要比上一层的小,所以还有一个最大值备选项:
1 | r[now + 1] - 1 |
所以最终r的取值范围:
1 | for (r[now] = min((int)sqrt(((double)n - v) / now), r[now + 1] - 1); |
h同理得到
1 | for (h[now] = min((int)((double)(n - v) / r[now] / r[now]), h[now + 1] - 1); |
卡常与可行性剪枝
1 ~ now的最小体积加上v一定小于等于n,不然就没有意义了,按道理应该这么写:
1 | if (v + minv[now] > n) continue; |
上述写法会把now的改变留到下一次递归判断,正常的DFS也确实是这么标准的模板——
但无疑这样会增加多余的栈开销,本题也会TLE,遂改成:
1 | ll tmpv = r[now] * r[now] * h[now]; |
最优性剪枝
1 ~ now的最小侧面积加上s一定小于等于ans,不然没必要继续搜下去,所以有:
1 | ll tmps = 2 * r[now] * h[now]; |
我们的r和h都是利用n - v来剪枝的,尝试利用不等式放缩来剪枝,1 ~ now的最小侧面积和有不等式:
\[
2\sum{\begin{array}{c}
now\\
k=1\\
\end{array}}r\left[ k \right] \cdot h\left[ k \right] =\frac{2}{r\left[
now \right]}\sum{\begin{array}{c}
now\\
k=1\\
\end{array}}r\left[ k \right] \cdot h\left[ k \right] \cdot r\left[ now
\right] \geqslant \frac{2}{r\left[ now \right]}\sum{\begin{array}{c}
now\\
k=1\\
\end{array}}r\left[ k \right] ^2\cdot h\left[ k \right]
=\frac{2}{r\left[ now \right]}\cdot \left( n-v \right)
\] 也就是说,1 ~ now的侧面积和最小值为\(\frac{2\left( n-v \right)}{r\left[ now
\right]}\),可以得到:
1 | if (s + (double)2 * (n - v) / r[now] > ans) continue; |