POJ1190===搜索+剪枝===生日蛋糕

/*搜索+剪枝*/
#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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值