一道很经典也很基础的搜索题目。有题目的条件我们可以得到两个方程:sum(Rk*Rk*hk) = N 和 S = sum(2*Rk*hk)+R1*R1
首先考虑极端剪枝法。假设前i层体积为T,如果剩下的若干层,每层都去最小可能值,体积仍比N大,则剪去,如果剩下几层都去最大值,体积仍比N小,也减去。如果当前的表面积比S大,则剪去。
然后考虑一个隐藏的条件,我们可以由式子sum(Rk * Rk * hk) = N来考虑。假设前i层的体积为T * PI,表面积为W * PI ,那么剩余m-i层的体积满足: N—T = sum(Rk * Rk *hk),其中i+1 <= k <= m ,而剩余的表面积为:leftS = sum(2*Rk * hk) i+1<=k<=m ,lefts = 2sum(Rk*Rk*hk/Rk) >= 2*sum(Rk*Rk*hk/Ri) = 2*(N-T)/Ri= p ;如果p>= S-w
那么lefts >= s-w ,于是我们在这里也可以加以剪枝。只需要判断一下即可。(参考了刘汝佳的算法艺术与信息学竞赛)
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
using namespace std ;
int N ;
int M ;
int S ;
int nmin ;
//leftv 表示剩余的体积 ,leftm剩余的层数
//lastr上一层的半径,lasth上一层的高度
void dfs(int leftv , int leftm , int lastr , int lasth){
if(!leftm){
if(leftv != 0)
return ;
if(nmin < S)
S = nmin ;
return ;
}
//计算当前条件下最小的体积
int t ;
t = 0 ;
for(int i = leftm ; i > 0 ; i--){
t += i * i * i ;
}
//剩余的最小体积仍大于剩下的体积
if(t > leftv)
return ;
//计算剩下的最大半径
int maxr ;
int maxh ;
t -= leftm * leftm * leftm ;
maxr = (int)sqrt( 1.0 * (leftv - t)/leftm ) < lastr ? (int) sqrt(1.0 * (leftv - t)/leftm ) : lastr ;
for(int r = maxr ; r >= leftm ; r --){
maxh = (leftv - t) / (r*r) < lasth ? (leftv - t)/(r*r) : lasth ;
for(int h= maxh ; h >= leftm ; h --){
int maxv ;
maxv = 0 ;
//计算最大体积
for(int i = 0 ; i <= leftm -1 ; i ++){
maxv += (r - i) * (r - i) * (h - i) ;
}
if(maxv < leftv){
break ;
}
//如果是第一层,则加上上表面积
if(leftm == M){
if(r * r < S)
nmin = r * r ;
else
continue ;
}
if(nmin + 2 * r * h > S){
continue ;
}
int v ;
v = leftv - r * r * h ;
//对应于第二种情况的剪枝
if(2 * v / r >= S - nmin - 2 * r * h)
continue ;
nmin += 2 * r * h ;
dfs(v , leftm - 1 , r - 1 , h - 1) ;
nmin -= 2 * r * h ;
}
}
}
int main(){
while(scanf("%d%d" , &N , &M) != EOF){
nmin = 0 ;
S= 1000000 ;
dfs(N , M , 10000 , 10000) ;
printf("%d\n" , S) ;
}
return 0 ;
}