1. 程式人生 > >搜尋_DFS_POJ1190_生日蛋糕

搜尋_DFS_POJ1190_生日蛋糕

點此開啟題目頁面

思路分析:

    考慮使用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;
}