题意:一个由多个圆柱体摞起来形成的蛋糕,要求上边的圆柱的半径和高都比下边的圆柱大。给定圆柱的体积,问除去下表面之外的圆柱面积最小是多少?
分析:深度优先搜索。由最底层的蛋糕向上逐层搜索,每次迭代则蛋糕向上一层。所以搜索深度确定,为蛋糕的层数。
与通常的深度优先搜索不同的是,本题的每个搜索状态由两个量来标明:半径和高。因此向下一个状态迭代时需要用一个二重循环来枚举接下来状态的半径和高。
可见状态空间极大。需要三个剪枝方可通过本题。
我们首先要了解,位于蛋糕顶端的x层的最小体积是:1^3+2^3+3^3+...+x^3
最小面积是:2*(1^2+2^2+3^2+...+x^2)
即从顶层向下半径依次取1~x,高依次取1~x。
三个剪枝是:
1.总体积减去蛋糕当前层以下的层的总体积是否小于上面的层所能构成的最小体积。如果小于则返回。
2.已算得的答案(最小面积)减去蛋糕当前层以下的层的总面积是否小于上面的层所能构成的最小面积。如果小于则返回。
3.设剩余总体积为总体积减去蛋糕下面的层的总体积,剩余总面积是当前已得最优解减去蛋糕当前层以下的层的总面积。设ri为每层的半径,hi为每层的高度。蛋糕上面层的总面积是对2*ri*hi求和。总体积ri*ri*hi求和。剩余总体积*2再除以当前层的半径必须小于剩余总面积,否则返回。(2×sigma(ri*ri*hi)/rk > sigma(2*ri*hi) i=1~k)之所以左边大于右边是因为rk>ri 对于i<k。
#include <cstdio> #include <algorithm> #include <cmath> using namespace std; #define MAX_LAYER_NUM 25 #define INF 0x3f3f3f3f int layer_num; int total_volume; int min_vol[MAX_LAYER_NUM]; int min_area[MAX_LAYER_NUM]; int ans; void init() { min_area[0] = min_vol[0] = 0; for (int i = 1; i <= 20; i++) { min_vol[i] = min_vol[i - 1] + i * i * i; min_area[i] = min_area[i - 1] + 2 * i * i; } } void DFS(int r_limit, int h_limit, int layer, int area, int volume) { if (layer == 0) { if (volume != total_volume) return; ans = min(ans, area); return; } if (total_volume - volume < min_vol[layer]) return; if (ans - area < min_area[layer]) return; if (layer < layer_num && area + 2 * (total_volume - volume) / r_limit > ans) return; for (int i = r_limit; i >= layer; i--) { if (layer == layer_num) { area = i * i; } int max_h = (total_volume - min_vol[layer - 1] - volume) / (i * i); for (int j = min(max_h, h_limit); j >= layer; j--) { DFS(i - 1, j - 1, layer - 1, area + 2 * i * j, volume + i * i * j); } } } int main() { scanf("%d%d", &total_volume, &layer_num); ans = INF; init(); DFS((int)sqrt(total_volume), total_volume, layer_num, 0, 0); printf("%d ", ans); return 0; }