luogu P1731 生日蛋糕

搬运工…………

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\n",ans%0x3f3f3f3f);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/fpjo/p/11405935.html