http://poj.org/problem?id=1190
打表每层的最小面积和体积。
由于深度一定(m),所以使用深度优先搜索,自上而下的设定蛋糕序号,最顶层的为第1层,……,最底层的蛋糕为第m层,很明显满足题目条件的前i层的(从顶层(也就是编号为1的层)开始计数)最小面积mins[i]和体积minv[i]是在该层的半径以及高度都为i时取得,如果采用一般的神搜肯定会超时,所以这题还需要剪枝,剪枝条件有(从m层向上搜,假设前dep层的体积为sv,面积为ss,当前所得的最小面积为best)。
三个剪枝:
一、因为前dep层的体积为sv,如果剩下的几层的体积都取最小可能值,总体积还是比n大,那么则说明前dep层的方案不可行,所以可以剪枝(剪枝条件为:sv+minv[dep-1]>n)
二、因为前dep层的面积为ss,如果剩下的几层的面积都取最小可能值,所得的面积和比已经得到的所求的最小面积best大,也可以进行剪枝(剪枝条件为:ss+mins[dep-1]>best)
三、(关键)因为前dep层的体积为sumv,那么剩余的m-dep层的体积满足:n-sv=(h[k]*(r[k]^2)+……+h[m]*(r[m]^2))
而剩余部分的表面积满足:lefts=2*(r[k]*h[k]+……+r[m]*h[m])>2*(n-sv)/r[dep]
显然有上述不等式lefts=best-ss>2*(n-sv)/r,即2*(n-sv)/r+ss<best,所以当2*(n-sv)/r[i]+ss>=best时也可以进行剪枝. (即不等式的放缩)
在判断高度的时候,枚举了半径,判断最小高度,即minh=min((n-sv-minv[deep-1])/(i*i),h-1);,没有此判断会超时。
以n+1为半径和高度的起点。当为1时,极限为n+1。
1 #include<cstdio> 2 #include<iostream> 3 using namespace std; 4 #define INF 10000000 5 int best,n,m; 6 int mins[23],minv[23]; 7 void biao() 8 { 9 int i; 10 mins[0]=0; 11 minv[0]=0; 12 for(i=1;i<22;i++) 13 { 14 mins[i]=mins[i-1]+2*i*i; 15 minv[i]=minv[i-1]+i*i*i; 16 } 17 } 18 void dfs(int deep,int ss,int sv,int r,int h) 19 { 20 int i,j,minh; 21 if(deep==0) 22 { 23 if(ss<best&&sv==n) 24 best=ss; 25 return; 26 } 27 if(sv+minv[deep-1]>n||ss+mins[deep-1]>best||2*(n-sv)/r+ss>=best) 28 return; 29 for(i=r-1;i>=deep;i--) 30 { 31 if(deep==m) 32 ss=i*i; 33 minh=min((n-sv-minv[deep-1])/(i*i),h-1); 34 for(j=minh;j>=deep;j--) 35 dfs(deep-1,ss+2*i*j,sv+i*i*j,i,j); 36 } 37 } 38 main() 39 { 40 biao(); 41 while(~scanf("%d%d",&n,&m)) 42 { 43 best=INF; 44 dfs(m,0,0,n+1,n+1); 45 if(best==INF) 46 best=0; 47 printf("%d\n",best); 48 } 49 }