链接:
http://poj.org/problem?id=1190
思路:
剪枝。
1、从半径n+1开始
2、当前已有表面积,加上剩余的预估最小表面积,若大于最优解,减掉。(什么是预估最小解?高度每次减一,半径也每次减一就是了,这个可以求出来)
3、当前已有体积,加上剩余的预估最小体积,若大于最优解,减掉(同上)。
4、搜索途中,若体积超出限制,减掉
5.(目标体积-已有体积)/r*2+已有表面积>=ans,这个剪枝其实是把体积和面积预估合起来了,是说如果把剩下的所有体积,全部用来只做成一个圆柱体,这种情况加所增加到表面积是最小的。 为啥呢?因为此时是以r为底算的,底盘最大,高度最低,简单点说,你觉得相同体积下,是个瘦高的侧面积大,还是矮胖的大?
推导公式:
2*r*h=S
r*r*h=V
=> V*2/r = S
S + sumS >= best => return;
题解:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int maxn = 30;
int ms[maxn],mv[maxn];
int ans;
int n,m;//体积和层数
void dfs(int summ,int v,int dep,int h,int r)//当前总面积,当前体积,剩余层数,剩余高度,当前半径
{
if(v>n)
return;
if(!dep)
{
if(v==n && summ<ans) ans = summ;
return;
}
if(v+mv[dep]>n||summ+ms[dep]>ans||(n-v)/r*2+summ>=ans) return;
for(int i=r-1;i>=dep;i--)//枚举半径
{
if(dep==m)
summ=i*i;
int maxh=min((n-v-mv[dep-1])/(i*i),h-1);//剩余部分在该层的最大高度
for(int j=maxh;j>=dep;j--)
dfs(summ+2*i*j,v+i*i*j,dep-1,j,i);
}
return;
}
void init()
{
for(int i=1;i<=20;i++)
{
ms[i]=ms[i-1]+i*i*2;
mv[i]=mv[i-1]+i*i*i;
}
return ;
}
int main()
{
init();
int i,j;
cin>>n>>m;
ans = 1e9;
dfs(0,0,m,n+1,n+1);//注意从n+1开始的
if(ans == 1e9)
ans=0;
cout<<ans<<endl;
return 0;
}