----Frosty_Jackal于9.13
直接把k当成容器V,ti当成物品,跑一遍《放与不放》的DFS即可。从大物品向小物品枚举能更快搜到答案,方便剪枝。
代码:
void dfs(int x,int y)
{
int i;
if(ans <= y) return ;
if(x==n+1)
{
if(ans>y)
ans=y;
return ;
}
for(int i=1;i<=k;++i)
{
if(s[i]+a[x]<ans)
{
s[i]+=a[x];
dfs(x+1,max(y,s[i]));
s[i]-=a[x];
}
}
return ;
}
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
sort(a+1,a+1+n);
reverse(a+1,a+1+n);
ans=2147483647;
dfs(1,0);
printf("%d",ans);
return 0;
}
思路:从下往上枚举高度与半径,由于蛋糕的表面积=所有侧面积+底下的蛋糕的上表面积,于是我们只需要关心侧面积的大小。
注意,对于第i层,是指从上往下数第i层。下面省去π
剪枝:1.从下往上枚举,由于我们可以预处理出前i层所需的最小侧面积与体积(当且仅当最上面的半径和高度为1且下面的之比上面的高度高1,半径长1),所以当S(now)+Smin还>=已搜到ans,则直接return,不再搜索。当v(Now)+Vmin>n,直接return,不再搜索。
- 可以限制枚举蛋糕的最大高度,MAXN_high为min(h-1,(n-v-minv[now-1])/i/i)
- 对于未来1~dep层,它的侧面积之和为 i=1~dep h[i]*r[i]*2 , 可以化为 i=1~dep h[i]*r[i]/r[dep]*2*r[dep] >= i=1~dep h[i]*r[i]的平方 /r[dep]*2 = v[未来1~dep层]*2/r[dep]
=n- v(dep+1 ~ 底层)*2/r[dep]
所以 当 s(已搜到) + n- v(dep+1 ~ 底层)*2/r[dep] > 已搜到的ans 时 ,return。
代码:
#include<bits/stdc++.h>
#define Q printf("(!)");
using namespace std;
const int MAXN=0x3f3f3f;
const int N=20007;
int ans=MAXN;
int mins[N],minv[N],n,m;
void dfs(int now,int r,int h,int s,int v)
{
int MAXN_high=h;
if(now==0)
{
if(v==n)
{
ans=min(ans,s);
}
return ;
}
if(s+mins[now-1]>=ans) return ;
if(v+minv[now-1]>n) return ;
if(2*(n-v)/(r)+s>=ans) return ;
for(int i=r-1;i>=now;i--)
{
if(now==m) s=i*i;
MAXN_high=min(h-1,(n-minv[now-1]-v)/i/i);
for(int j=MAXN_high;j>=now;j--)
{
dfs(now-1,i,j,s+2*i*j,v+i*i*j);
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;++i)
{
mins[i]=mins[i-1]+2*i*i;
minv[i]=minv[i-1]+i*i*i;
}
dfs(m,n,n,0,0);
if(ans==MAXN) cout<<0<<endl;
else cout<<ans<<endl;
return 0;
}