题目解析:给出蛋糕的总体积 n 和蛋糕层数 m,求最佳的各层蛋糕的半径和高度分配方案,使得蛋糕表面积最小。

题目条件理解:对各层蛋糕进行编号,自下而上,最底层编号为1,最高一层编码为m。对于半径和高度存在约束,Ri > Ri+1 和 Hi > Hi+1。
蛋糕表面积的相关计算:
第1层~第m层顶端表面积(即圆柱体上表面)等价于 第1层的底面面积
总表面积 = 第1层的底面面积 + sum(第1层~第m层的侧面积)

问题求解
这是一道搜索类型的题目,常规做法是从第一层蛋糕开始,逐层向上搜索,直至搜索到最优解。
但是第一层蛋糕的半径和高度没有明确给出上下限,需要我们自己推导。
由于第i+1层蛋糕的半径和高度 必须大于 第i层蛋糕的半径和高度,由此可推导出第1层蛋糕半径的下限为m。(注:极端情况下,第一层的半径为1,第二层的半径为2,…,第m层的半径为m)
同理,第1层的高度的下限为m
那第一层的半径上限如何确定呢?这个需要借助已经给出的蛋糕总体积n,我们的想法是从第一层开始逐层枚举,那么第2层~第m层同样是会占据一部分体积,那么要求第一层在分配的时候,不能把体积都分配完,所以由此推出第一层蛋糕半径的上限r * r * m <= n(注:r为待枚举的第一层半径高度)。
同理,第1层的高度的上限r * r * h <= n(注:h为待枚举的第一层高度)。
下面给出搜索框架:

  1. for(int r = m; r * r * m <= n; r++){
  2. for(int h = m; r * r * h <= n; h++){
  3. if(r * r + 2 * r * h < ans){//只有有机会搜索到更优的解进行递归
  4. //确定第一层蛋糕的半径和高度,逐层往上搜索
  5. //dfs()细节待补充
  6. dfs();
  7. }
  8. }
  9. }

接下来谈谈如果逐层搜索以及如何剪枝。

  1. public static void dfs(int depth, int v, int s, int r, int h){
  2. //暂时不考虑剪枝的细节
  3. //由于第i+1层的高度和半径 < 第i层的高度和半径
  4. //故第i+1层的高度和半径的下限分别为r-1;(此处r为第i层的半径, h为第i层的高度)
  5. //由于必须保证第m层的最小半径和高度为1,所以借此确定第i+1层的半径和高度的上限
  6. //并且需要判断当前选取的半径i和高度j不能超过总体积和小于最优解,才可再往上一层搜索
  7. for (int i = r -1 ; i >= m - depth; i--){
  8. for (int j = h - 1; j >= m - depth; j--){
  9. if((i * i * j + v <= n) && (s + 2 * i * j < ans)){
  10. dfs(depth + 1, v + i * i * j, s + 2 * i * j, i ,j);
  11. }
  12. }
  13. }
  14. }

如果不考虑剪枝,直接爆搜,会超时,所以加入两个剪枝操作。
● 假设现在搜索到第 depth 层,接下来的 M - depth层的高度和半径均取最大值 h-1 和 r-1,仍然小于总体积n,则剪枝,代码为:v + (r-1)*(r-1)*(h-1)*(m-depth) < n。(注:v为搜索到第 depth 层累计的体积,r 和 h 为 第 depth层所确定的半径和高度)
● 假设现在搜索到第 depth 层,接下来的 M - depth层的高度和半径均取最小值1,仍然大于总体积n,则剪枝,代码为:v + m - depth > n。(注:v为搜索到第 depth 层累计的体积,r 和 h 为 第 depth层所确定的半径和高度,在代码处做了化简,原始代码:v + 1 * 1 * 1 *m - depth > n

完整代码

  1. import java.util.*;
  2. public class Test{
  3. public static int n, m;
  4. public static int ans = Integer.MAX_VALUE;
  5. public static void dfs(int depth, int v, int s, int r, int h){
  6. if(depth == m){
  7. if(v == n){
  8. ans = s;
  9. }
  10. return;
  11. }
  12. if(v + (r-1)*(r-1)*(h-1)*(m-depth) < n){
  13. return;
  14. }
  15. if(v + m - depth > n){
  16. return;
  17. }
  18. for (int i = r -1 ; i >= m - depth; i--){
  19. for (int j = h - 1; j >= m - depth; j--){
  20. if((i * i * j + v <= n) && (s + 2 * i * j < ans)){
  21. dfs(depth + 1, v + i * i * j, s + 2 * i * j, i ,j);
  22. }
  23. }
  24. }
  25. }
  26. public static void main(String[] args) {
  27. Scanner sc = new Scanner(System.in);
  28. n = sc.nextInt();
  29. m = sc.nextInt();
  30. for(int r = m; r * r * m <= n; r++){
  31. for(int h = m; r * r * h <= n; h++){
  32. if(r * r + 2 * r * h < ans){
  33. dfs(1, r * r * h, r * r + 2 * r * h, r, h);
  34. }
  35. }
  36. }
  37. System.out.println(ans);
  38. }
  39. }

参考资料https://blog.csdn.net/qq_42367531/article/details/85055165?utm_term=%E4%B8%80%E6%9C%AC%E9%80%9A1674%E5%A0%86%E8%9B%8B%E7%B3%95&utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2~all~sobaiduweb~default-0-85055165&spm=3001.4430

点赞(0)
 

9.9 分

2 人评分

 

C语言网提供由在职研发工程师或ACM蓝桥杯竞赛优秀选手录制的视频教程,并配有习题和答疑,点击了解:

一点编程也不会写的:零基础C语言学练课程

解决困扰你多年的C语言疑难杂症特性的C语言进阶课程

从零到写出一个爬虫的Python编程课程

只会语法写不出代码?手把手带你写100个编程真题的编程百练课程

信息学奥赛或C++选手的 必学C++课程

蓝桥杯ACM、信息学奥赛的必学课程:算法竞赛课入门课程

手把手讲解近五年真题的蓝桥杯辅导课程

评论列表 共有 0 条评论

暂无评论