搜索_DFS_POJ1190_生日蛋糕

版权声明:本文为博主原创作品, 转载请注明出处! https://blog.csdn.net/solider98/article/details/84064720

点此打开题目页面

思路分析:

    考虑使用DFS, 从最底层开始枚举每层的半径和高度, 使用如下剪枝策略:

    (1)设第1... i - 1层的体积之和为v, 表面积为p, 第i - 1层的半径为R, 高度为H, 对于第i层(1 <= i  <= M, 最底层为第1层), 设t = M - i + 1, 那么的第i层半径r满足: t <= r < min(R - 1, \sqrt{N - v}), 第i层的高度h满足, t <= h <= min(H - 1, (N - v) / r^{2})

    (2)从大到小枚举半径和高度

    (3)根据s = 2\sum_{k = i}^{M}r_{k}h_{k}=\frac{2}{r_{i}}\sum_{k = i}^{M}r_{k}h_{k}r_{i}>=\frac{2}{r_{i}}\sum_{k = i}^{M}r_{k}^{2}h_{k}=\frac{2}{r_{i}}(N-v), 可知p + s>已经计算出的最优解, 那么直接回溯.

    (4)预处理第j至M(1 <= j <= M)层的体积最小值和表面积最小值, 如果在当前选择的半径和高度使得对应的在剩余层次取表面积最小值也大于当前最优解时则直接回溯. 如果当前枚举方案对应的体积加上剩余层次的最小体积时大于N, 那么直接回溯>

    基于上述剪枝方案, 给出如下AC代码:

//POJ1190_生日蛋糕
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
const int MAXM = 25, NIL = 0x3f3f3f3f;
int N, M, res, R;//R最优方案对应的第一层半径 
int mins[MAXM], minv[MAXM];//mins[i]:i...M层侧面积的最小值, mins[i]:i...M层的体积的最小值 
//当前正在拼接第now层, 已拼接好部分的底面积加上面积为nows, 体积为nowv
//lastR: 第now - 1层的半径, lastH:高度 
void dfs(int now, int nowv, int nows, int lastR, int lastH){
	if(now > M){
		if(nowv == N) res = min(res, nows); return;
	}	
	for(int r = min((int)sqrt((double)N - nowv), lastR - 1); r >= M - now + 1; --r){
		if(now == 1) nows = r * r;
		if(((double)2 * (N - nowv)) / r + nows >= res) continue;
		for(int h = min((N - nowv) / (r * r), lastH - 1); h >= M - now + 1; --h){			
			int tmps = nows + 2 * r * h, tmpv = nowv + r * r * h;
			if(tmps + mins[now + 1] >= res || tmpv + minv[now + 1] > N) continue;
			dfs(now + 1, tmpv, tmps, r, h);
		} 
	}
}
int main(){
	scanf("%d %d", &N, &M);
	for(int i = M, rh = 1; i >= 1; --i, ++rh) 
		mins[i] = mins[i + 1] + 2 * rh * rh, minv[i] = minv[i + 1] + rh * rh * rh;
	res = NIL, dfs(1, 0, 0, NIL, NIL), cout << res<< endl;	
	return 0;
} 

    

猜你喜欢

转载自blog.csdn.net/solider98/article/details/84064720
今日推荐