[poj1190][搜索]生日蛋糕

Description

7月17日是Mr.W的生日,ACM-THU为此要制作一个体积为Nπ的M层生日蛋糕,每层都是一个圆柱体。 设从下往上数第i(1 <= i
<= M)层蛋糕是半径为Ri, 高度为Hi的圆柱。当i < M时,要求Ri > Ri+1且Hi > Hi+1。
由于要在蛋糕上抹奶油,为尽可能节约经费,我们希望蛋糕外表面(最下一层的下底面除外)的面积Q最小。 令Q = Sπ
请编程对给出的N和M,找出蛋糕的制作方案(适当的Ri和Hi的值),使S最小。 (除Q外,以上所有数据皆为正整数)

Input

有两行,第一行为N(N <= 10000),表示待制作的蛋糕的体积为Nπ;第二行为M(M <= 20),表示蛋糕的层数为M。

Output

仅一行,是一个正整数S(若无解则S = 0)。

Sample Input

100 2

Sample Output

68

Hint

圆柱公式 体积V = πR2H 侧面积A’ = 2πRH 底面积A = πR2

题解

搜索的奇技淫巧系列
首先表面积一定包含了最底下那个圆柱的圆面积,我们只需要计算侧面积再加上那个圆面积就好
从底往上搜,设最底端圆柱编号为1且编号向上递增,然后开始一通瞎剪
设当前枚举到编号为k的圆柱,下面的体积和为v,侧面积和为s
用r[i],h[i]记录编号为i的圆柱半径及高度
1:对于搜索当前答案>=最小答案直接剪掉这不是废话吗
2:枚举半径的时候从大往小枚举为什么后面再说
3:对于半径的枚举,我们限制范围为[k,min(sqrt(nv)),r[k1]1][k,min(sqrt(n−v)),r[k−1]−1]
4:对于高度的枚举,我们限制范围为[k,min((nv)/R2,h[k1]1)][k,min((n−v)/R2,h[k−1]−1)],其中R是枚举的半径
5:推一个柿子
对于第i个圆柱,他和他上面的圆柱体积和一定要为n-v,可替代为sigma(r[i]2h[i])sigma(r[i]2∗h[i])
同理,表面积和即为2sigma(r[i]h[i])2∗sigma(r[i]∗h[i])
处理一下表面积的柿子,可以得到2/r[k]sigma(r[i]r[k]h[i])2/r[k]∗sigma(r[i]∗r[k]∗h[i])
再处理第一个柿子,可以得到2/r[k]sigma(r[i]2h[i])=(nv)2/r[k]2/r[k]∗sigma(r[i]2∗h[i])=(n−v)∗2/r[k]
由于r[k]一定大于任意一个r[i],所以有
2/r[k]sigma(r[i]r[k]h[i])>=(nv)2/r[k]2/r[k]∗sigma(r[i]∗r[k]∗h[i])>=(n−v)∗2/r[k]
由于第一式代表的实际为第i个圆柱及其上面的圆柱的侧面积和,所以他+s一定要小于最小答案,但是他比较难算出所以不考虑这里剪枝。由于后面那个式子可以快速算出,且他不会大于第一式。那么在满足(nv)2/r[k]+s>=ans(n−v)∗2/r[k]+s>=ans时,第一式也一定满足,于是可以通过这个剪枝
然后你就可以发现,为什么要倒着枚举了

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
int n,m,ans;
int h[11000],r[11000];
int mncos[11000];
void dfs(int k,int s,int v)
{
    if(k==m+1)
    {
        if(v==n)ans=min(ans,s);
        return ;
    }
    int u=min(int(double(sqrt(n-v))),r[k-1]-1);
    for(int i=u;i>=(m-k+1);i--)
    {
        if(2*(n-v)/i+s>=ans)continue;
        for(int j=(m-k+1);j<=min((n-v)/(i*i),h[k-1]-1);j++)
        {
            if(s+2*i*j+mncos[k+1]>=ans)break;
            if(v+i*i*j>n)break;
            r[k]=i;h[k]=j;
            if(k!=1)dfs(k+1,s+2*i*j,v+i*i*j);
            else dfs(k+1,s+2*i*j+i*i,v+i*i*j);
            r[k]=h[k]=0;
        }
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=m;i>=1;i--)mncos[i]=mncos[i+1]+2*(m-i+1)*(m-i+1);
    ans=999999999;r[0]=h[0]=999999999;
    dfs(1,0,0);
    if(ans==999999999)printf("0\n");
    else printf("%d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值