概述
Description
7月17日是Mr.W的生日,ACM-THU为此要制作一个体积为Nπ的M层生日蛋糕,每层都是一个圆柱体。
设从下往上数第i(1 <= i <= M)层蛋糕是半径为Ri, 高度为Hi的圆柱。当i < M时,要求Ri > Ri+1且Hi > Hi+1。
由于要在蛋糕上抹奶油,为尽可能节约经费,我们希望蛋糕外表面(最下一层的下底面除外)的面积Q最小。
令Q = Sπ
请编程对给出的N和M,找出蛋糕的制作方案(适当的Ri和Hi的值),使S最小。
(除Q外,以上所有数据皆为正整数)
设从下往上数第i(1 <= i <= M)层蛋糕是半径为Ri, 高度为Hi的圆柱。当i < M时,要求Ri > Ri+1且Hi > Hi+1。
由于要在蛋糕上抹奶油,为尽可能节约经费,我们希望蛋糕外表面(最下一层的下底面除外)的面积Q最小。
令Q = Sπ
请编程对给出的N和M,找出蛋糕的制作方案(适当的Ri和Hi的值),使S最小。
(除Q外,以上所有数据皆为正整数)
Input
有两行,第一行为N(N <= 10000),表示待制作的蛋糕的体积为Nπ;第二行为M(M <= 20),表示蛋糕的层数为M。
Output
仅一行,是一个正整数S(若无解则S = 0)。
Sample Input
100
2
Sample Output
68
样例:
#include <iostream>
#include <stdio.h>
#include <cmath>
using namespace std;
int n,m,minv[21],mins[21];
//V=n*pi
m 层数 自顶向下1.2.3...m
//minv[i]表示i层到0层加起来的最小总体积 minvs 最小表面积
const int inf=1000000000;
// inf 足够大就可以 int(32)
-2^31~2^31-1=2147483647
int best;
//best 最小表面积
void DFS(int depth,int sumv,int sums,int r,int h) //深度优先搜索 自底m向上搜索 r h表示当前层得半径和高度
//sumv已经用的总体积 sums已经生成的总表面积
{
if(0==depth)
{
if(sumv==n&&sums<best)
//搜索完成 更新最小表面积best
best=sums;
return;
}
// 三个剪枝条件:
//1、已经搜索过的体积加上还未搜索过的最小体积不能比总体积n 大
//2、已经搜索过的表面积加上还未搜索过的最小表面积不能比之前的最小总表面积best 大
//3、n-sumv既所剩体积记作dv 还需要的表面积为s
//s=2*ri*hi+2*r(i-1)*h(i-1)+... >=2*ri*hi*ri/r+2*r(i-1)*h(i-1)*r(i-1)/r+...
//
=2*dv/r(i从depth-1取,r为当前半径 ri/r<1)
// 所以得到还需要的最小表面积s=2*(n-sumv)/r,如果最小的s和已经搜索过的表面积sums依然比best大 就不用继续搜索了
if(sumv+minv[depth-1]>n||sums+mins[depth-1]>best||sums+2*(n-sumv)/r>=best)
//剪枝如上所述
return;
for( int i=r-1;i>=depth;i--)
//递减顺序枚举depth层半径的每一个可能值,这里第depth层的半径最小值为depth
{
if(depth==m)
sums=i*i;
//俯视蛋糕底面积作为外表面积的初始值(总的上表面积,以后只需计算侧面积)
int maxh=((n-sumv-minv[depth-1])/(i*i)<h-1)? (n-sumv-minv[depth-1])/(i*i):h-1;
//maxh最大高度,即depth层蛋糕高度的上限,(n-sumv-minv[dep-1])表示第depth层最大的体积
for(int j=maxh;j>=depth;j--)
//同理,第depth层的最小高度值为depth
DFS(depth-1,sumv+i*i*j,sums+2*i*j,i,j);
//递归搜索子状态
}
}
int main(void)
{
while(scanf("%d%d",&n,&m)!=EOF)
{
best=inf;
int rmax=(int)sqrt((double)n); //rmax初始半径 底层半径 最大值为sqrt(n)
int hmax=n;
//hmax初始高度 高度最大为 n
minv[0]=mins[0]=0;
for(int i=1;i<=m;i++)
{
//初始化minv和mins数组
minv[i]=minv[i-1]+i*i*i;
//从顶层(即第1层)到第i层的最小体积minv[i]成立时第j层的半径和高度都为j
mins[i]=mins[i-1]+2*i*i;
}
DFS(m,0,0,rmax,hmax);
if(best==inf)
best=0;
//无解
if(best==0)
cout<<0<<endl;
else
cout<<best<<endl;
}
return 0;
}
样例二:
#include <iostream>
#include <cmath>
#include <cstdio>
using namespace std;
const int inf=0xfffffff ;
int preminv[21] ;
int ri[21],hi[21] ;
int total,floornum ;
int ans ;
int dfs (int n,int v,int s,int maxr,int maxh)
{
int res ;
int i,tmpr,tmph,tmpsum ;
if (n>floornum){
if (v==total && s+ri[1]*ri[1]<ans){
ans = s+ri[1]*ri[1] ;
return 1 ;
}
}
for (ri[n]=maxr-1 ; ri[n]>=floornum-n+1 ; ri[n]--){
res = 2.0*(total-v)/ri[n] + ri[1]*ri[1] ;
if (res + s >= ans) continue ;
for (hi[n]=maxh-1 ; hi[n]>=floornum-n+1 ; hi[n]--){
if (v+ri[n]*ri[n]*hi[n]+preminv[floornum-n]>total)
continue ;
tmpr = ri[n] ,tmph = hi[n],tmpsum = 0 ;
for (i=n ; i<=floornum ; i++){
tmpsum += tmpr*tmpr*tmph ;
tmpr-- , tmph-- ;
}
if (v+tmpsum<total) continue ;
dfs(n+1,v+ri[n]*ri[n]*hi[n],s+2*ri[n]*hi[n],ri[n],hi[n]) ;
}
}
return 1 ;
}
int main()
{
int tmpv,tmpr,tmph ;
int i ;
tmpv=0 ;
for (i=1 ; i<21 ; i++){
tmpv += i*i*i ;
preminv[i] = tmpv ;
}
while (scanf("%d%d",&total,&floornum)!=EOF){
ans = inf ;
tmpv = total - preminv[floornum-1] ;
if (tmpv>0){
tmpr = sqrt (1.0*tmpv/floornum) + 1 ;
tmph = 1.0*tmpv/(floornum*floornum) + 1 ;
dfs(1,0,0,tmpr,tmph) ;
}
if (ans==inf){
ans = 0 ;
}
printf("%dn",ans) ;
}
return 0;
}
注意:此题搜索很容易办到,但是应利用合理剪枝来节省时间,而不管从层数,还是对于每层中r,h的枚举都应该遵从由大到小,因为如果从小到大的话,搜索更深更广容易超时.而如果是从大到小枚举的话,那么每次枚举出来的体积最大,剩余体积小,则可搜索下去的空间就小.即可省时.
最后
以上就是甜蜜咖啡为你收集整理的C语言DFS(3)___生日蛋糕(深度剪枝)的全部内容,希望文章能够帮你解决C语言DFS(3)___生日蛋糕(深度剪枝)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复