/*搜索+剪枝*/
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
int N, M; //N是体积 M是层数
int minArea = 1 << 30; //最优表面积
int area = 0; //正在搭建中的蛋糕的表面积
int minV[30]; //minV[n]表示(从上到下)n层蛋糕最少的体积
int minA[30]; //minA[n]表示(从上到下)n层蛋糕最少的侧表面积
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) {
//要用n层去凑体积v, 最底层半径不能超过r, 高度不能超过h
//最底层即将搭建的一层
//求出最小表面积放入minArea
if(n == 0) { //刚好搭建完了
if(v ) return ; //还留有体积需要搭建
else { //体积也刚好凑完
minArea = min(area, minArea);
return;
}
}
if( v <= 0) return ;//要凑得体积已经成了负值, 显然不能继续搭建
if(minV[n] > v) return ; //接下来要搭建的n层的最小值都比要凑得体积v大, 显然继续搭建下去浪费时间
if(area + minA[n] >= minArea) return ; //当前表面积加上接下来要搭建的n层的最少侧表面积都超过了已经计算出的最优表面积, 继续搭建下去, 也是浪费时间
if(h < n || r < n) return ; //根据最底层半径和高度判定若不能凑完接下来要搭建的v体积和n层 也直接返回
if(MaxVforNRH(n, r, h) < v) return ; //根据最底层半径和高度以及接下来n层判断如果全部搭建完毕也不能达到体积v 也返回。
for(int rr = r; rr >= n; --rr) { //搜索最底层的面积和体积 范围肯定是从最大值r和h到n, 如果小于n那么搭建完毕后肯定小于要求的层数或者体积
if(n == M) //底面积 N==M 是搭建第一层(最底层)的时候,要初始化area(当前表面积);
area = rr * rr;
for(int hh = h; hh >= n; --hh) {
area += 2*rr*hh;
//两层for循环遍历出每一组可能的rr和hh作为最底层的半径和高度
dfs(v - rr*rr*hh, n - 1, rr-1, hh-1); //下层的蛋糕半径和高度最少都比上层大 1。
//在进行下一组可能的情况时, 要先还原面积
area -= 2*rr*hh;
}
}
}
int main() {
cin >> N >> M; //M层蛋糕, 体积为N
minV[0] = 0;
minA[0] = 0;
for(int i = 1; i <= M; ++i) {
minV[i] = minV[i-1] + i*i*i;//从上往下数, 第i层的半径至少是i, 这一层的高度也最少为i,因为题目说明都是正整数
minA[i] = minA[i-1] + 2*i*i;
}
if(minV[M] > N) //如果从上往下数M层的最少体积都大于题目要求的N的话, 就直接否定
cout << 0 << endl;
else {
int maxH = (N - minV[M-1])/(M*M) + 1; //底层最大高度
//最底层最大体积不超过N-minV[M-1], 最小半径为M(半径>=M)
int maxR = sqrt(double(N-minV[M-1])/M) + 1; //底层最大半径
area = 0;
minArea = 1 << 30;
dfs(N, M, maxR, maxH);
if(minArea == 1 << 30) //最优表面积没有更新
cout << 0 << endl;
else
cout << minArea << endl;
}
return 0;
}
POJ1190===搜索+剪枝===生日蛋糕
最新推荐文章于 2022-11-09 09:35:48 发布