一道稍有难度的Dfs题,首先仔细寻找以下搜索节点的状态,也就是求在蛋糕有n层,底层最大半径为r,最高高度为h的情况下能凑出来的最大体积
接着要考虑一下搜索顺序,搜索范围和剪枝问题。顺序就好比玩七巧板,肯定是先把大的给定下来,也就是最低往上先大后小。
搜索范围最大不过只有两层,上层全部是1,其余全部放在底层。
剪枝的话这题中有很多可以剪枝的地方,因为数据量不是太大我只剪了几个,代码里已有注释写出~
//Poj1190 生日蛋糕
#include <iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<cmath>
using namespace std;
int N, M, minArea = 1<<30;
int area = 0; //成品蛋糕的表面积
int minV[25], minA[25];//n层蛋糕最小体积,侧面积
//搜索满足共有n层,体积为v,第n层半径r,高度h的状态
int MaxVforNRH(int n, int r, int h)
{ //求在蛋糕有n层,底层最大半径为r,最高高度为h的情况下能凑出来的最大体积
int v = 0;
for(int i = 0; i < n; i++)
v += (r-i) * (r-i) * (h-i);
return v;
}
void Dfs(int v, int n, int r, int h)
{
//要搭建体积为v的n层蛋糕,最底层半径不能超过r,高度不能超过h
if(n == 0){
if(v) return; //层数为0体积却不为0一定失败
else{ //搭建成功,返回最小表面积
minArea = min(minArea, area);
return;
}
}
if(v <= 0) return; //搭建失败
if(minV[n] > v) return; //可行性剪枝
if(area + minA[n] >= minArea) return; //最优性剪枝
if(h < n || r < n) return; //可行性剪枝
if(MaxVforNRH(n, r, h) < v) return; //可行性剪枝,提前预判当前能凑出的最大体积是否小于v
for(int rr = r; rr >= n; --rr){ //枚举底层半径
if(n == M)
area = rr*rr;
for(int hh = h; hh >= n; --hh){
area += 2*rr*hh;
Dfs(v-rr*rr*hh, n-1, rr-1, hh-1);
area -= 2*rr*hh;
}
}
}
int main()
{
scanf("%d%d",&N,&M);
minV[0] = 0; minA[0] = 0;
for(int i = 1; i <= M; ++i){
//第i层半径至少为i,高度至少为i
minV[i] = minV[i-1] + i*i*i;
minA[i] = minA[i-1] + 2*i*i;
}
if(minV[M] > N) printf("0\n"); //可行性剪枝
else{
int maxH = (N - minV[M-1])/(M*M)+1; //底层最大高度
//最底层体积不超过(N - minV[M-1]),且半径至少为M
int maxR = sqrt(double(N-minV[M-1]/M)/M) + 1; //底层高度至少为M
area = 0;
minArea = 1<<30;
Dfs(N, M, maxR, maxH);
if(minArea == 1<<30) printf("0\n");
else printf("%d\n",minArea);
}
return 0;
}