搬运工…………
DFS
蒟蒻搜索题学习+1
下面是思路:
DFS状态:当前层 dep 这时的外表表面积 s ,这时的体积 v ,每一层的半径 r[] ,每一层的高度 h[]
(普通的深搜肯定过不了,TLE)
所以要剪枝
1、r、h可以看出来会有取值范围:
r、h不可能小于dep(那当然哪,这是最小了!)
r、h不可能比下一层(r[dep+1]-1、h[dep+1]-1)还大(题目要求)
又有圆柱体体积公式,(这一层体积的最大值为n-v不是?)
所以
for(r[dep] = min((int)sqrt(n - v) , r[dep + 1] - 1); r[dep] >= dep; r[dep]--)
for(h[dep] = min((int)(double)((n-v) / r[dep] / r[dep]), h[dep + 1] - 1); h[dep] >= dep; h[dep]--)
(枚举范围剪枝)
2、我们可以来自上往下来想一想无n,m要求下1~i层最小的v情况会如何,那肯定是
r:1,2,3,4,5,6,······,i
h:1,2,3,4,5,6,······,i
v = π * r * r *h
**所以如果当层 v 加 1 ~ dep-1层的最小v 大于 n,就可以剪枝了**
(可行性剪枝)
3、另外想想,如果侧面积呢?
**
如果当前的表面积 加 1 ~ dep-1层的最小s 大于 自己之前搜到的答案,就可以剪枝了**
(最优性剪枝)
4、由3可以继续推,
设已经做了i层蛋糕,则还需做m-i层,
Si’:为第i层蛋糕的侧面积,
FSi:余下的侧面积
根据定义:
V=π*R*R*H(在这里统一删掉π)
则有:
2Vi= 2R[i+1] * R[i+i] * H[i+1] + ...+ 2Rm * Rm * Hm
(每一层的体积之和)
= R[i+1] * S[i+1]’ + ...+ Rm * Sm’
≤ R[i+1] * (S[i+1]’+ ...+ Sm’) 放缩法
= R[i+1]*FSi (剩余侧面积)
所以:
FSi ≥ 2Vi / Ri+1
引用自Fellyhosn这篇博客(自己懒得打了)
(最优性剪枝)
综上,我们可写出代码:
#include<bits/stdc++.h>
using namespace std;
int n,m,ans=0x3f3f3f3f;
int min_s[1000],min_v[1000],r[1000],h[1000];
template <typename T> inline void scan(T &x){
x=0;int f=1;char c=getchar();
while(c>'9'||c<'0'){if(c=='-')f=-f;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
x*=f;
}
void search(int dep,int s,int v){
if(dep==0){
if(v==n){
ans=min(s,ans);
}
return;
}
for(r[dep] = min((int)sqrt(n-v) , r[dep + 1] - 1) ; r[dep] >= dep ;r[dep]-- ){
for(h[dep] = min((int)(n-v) / r[dep] / r[dep] ,h[dep + 1] - 1) ;h[dep] >= dep ;h[dep]--){//1
if(v + min_v[dep-1] + r[dep] * r[dep] * h[dep]> n)continue;//2
if(s + min_s[dep - 1] + 2 * r[dep] * h[dep] > ans)continue;//3
if(s + 2 * (n - v) / r[dep] > ans )continue;//4
if(dep == m)s += r[dep] * r[dep];
search(dep - 1,s + 2 * r[dep] * h[dep],v + r[dep] * r[dep] * h[dep]);
if(dep == m)s -= r[dep]*r[dep];//注意,加底面积得要放在这里,如果放在最后会超时!!!
}
}
return;
}
int main(){
scan(n);scan(m);
for(int i=1;i<=m;i++){
min_s[i]=min_s[i-1]+2*i*i;
min_v[i]=min_v[i-1]+i*i*i;
}
r[m+1]=h[m+1]=0x3f3f3f3f;
search(m,0,0);
printf("%d
",ans%0x3f3f3f3f);
return 0;
}