1. 程式人生 > >POJ-1190 蛋糕問題

POJ-1190 蛋糕問題

這道題目我們使用深搜加剪枝的方法來寫,我們首先算出一個最小表面積和最小體積來,就是半徑從一遞增,高度也從一遞增,這是題目要求。

然後我們計算出一個底層最大的半徑和最大的高度,我們就從這個最大半徑和最大高度開始深度優先搜尋,每一層的蛋糕都從最大半徑開始深搜,然後內迴圈裡面同樣是對應每一層不同半徑不同高度的深搜,所以就是雙重迴圈進行搜尋。

但是在搜尋之前我們要進行剪枝,第一個剪枝是在邊界條件裡面,如果層數等於零了,但是v還不等與零,我們就直接返回,如果體積等於零了說明此方案可行,然後把它和最小表面積進行比較並賦值。

或者層數還不等於零,但是此時的體積已經小於零了,我們也直接返回。

其餘的剪枝都是在for迴圈裡面進行的,我們首先進行最優性剪枝,並且預測一下如果之前的表面積加上這層的表面積再加上之後最小的層數對應的表面積,如果此時大於了已經求得的最優表面積,我們就直接返回。

與此對應的可行性剪枝就是,如果總體積減去之前的體積,減去這層的體積,再減去之後層數的最小體積小於零的話,我們也直接返回。

如果,這層的最大半徑減去層數加一小於零了,或最大高度減去層數加一小於零了,我們也直接返回,因為每層的半徑和高度都要大於上一層,所以最小的公差就是一,如果最寬泛的這個頂層範圍都無法滿足的時候,我們就不必再搜了。

最後一個剪枝就是假設之後的每一層蛋糕都按照最大的半徑和高度建造,如果這時的體積依舊要小於我們要建的體積,我們也直接返回了。

因為這個深搜的範圍很寬,而且題目當中的時間限制是一秒,所以我們剪枝剪得精細一些才能過。

程式碼如下:

#include <iostream>
#include <cmath>
#include <cstring>
using namespace std;
int N,M;
int minArea=1<<30;
int area=0;
int minV[25];//k層及之上的蛋糕最小總體積
int minA[25];//k層及之上的蛋糕的最小表面積

void Dfs(int v,int n,int max_r,int max_h)
{
	if (n==0) {
		if (v!=0) {
			return ;
		}
		else {
			minArea=min(minArea,area);
			return ;
		}
	}
	if (v<0)
		return ;	
	for (int r=max_r;r>=n;r--) {
		if (n==M)
			area=r*r;
		for (int h=max_h;h>=n;h--) {
			if (area+2*r*h>=minArea)
				continue;
			if (v-r*r*h-minV[n-1]<0)
				continue;
			if (max_r-n+1<=0||max_h-n+1<=0)
				return ;
			int sum=0;
			for (int i=n-1;i>=0;i--) {
				sum+=(r-i)*(r-i)*(h-i);
			}
			if (sum<v)
				continue;
			area+=2*r*h;
			Dfs(v-r*r*h,n-1,r-1,h-1);
			area-=2*r*h;
		}
	}
}

int main()
{
	cin>>N>>M;
	int maxH,maxR;
	memset(minV,0,sizeof(minV));
	memset(minA,0,sizeof(minA));
	for (int i=1;i<=20;i++) {
		minA[i]=2*i*i+minA[i-1];
	}//側面積累加 
	for (int i=1;i<=20;i++) {
		minV[i]=minV[i-1]+i*i*i;//最小體積累加 
		minA[i]+=i*i;//最小表面積累加  
	}
	maxR=sqrt(N-minV[M-1]);
	maxH=N-minV[M-1];
	Dfs(N,M,maxR,maxH);
	if (minArea==1<<30)
		cout<<0<<endl;
	else
		cout<<minArea<<endl;
	return 0;
}