• sss


    <更新提示>

    <第一次更新>


    <正文>

    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 Format

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

    Output Format

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

    Sample Input

    100
    2
    

    Sample Output

    68
    

    解析

    预备知识:圆柱体体积公式(v=pi R^2H),圆柱体侧面积公式(s'=2pi RH),圆柱体底面积公式(s=pi R^2)

    输出和输入均不考虑(pi),所以直接无视所有公式中的(pi)即可。

    直接考虑一个搜索算法,状态为((dep,s,v)),代表当前搜索到第(dep)层(从上往下数),表面积为(s),体积为(v),并且记录了第(m)至第(dep-1)层的高度和半径为(h)(r)数组(小写)。然后对于每一层,枚举其高度(H)和半径(R),进行搜索。

    然后考虑如下的剪枝:

    • 上下界剪枝:枚举(R in [dep,min(lfloor sqrt{n-v} floor,r_{dep+1}-1)],Hin [dep,min(lfloor frac{n-v}{R^2} floor,h_{dep+1}-1)])

    由于蛋糕(dep)层以上还有(m-dep)层,高度和半径大小的要求都是递减的,所以枚举的左边界很容易得到。

    对于半径,其枚举右边界显然不能大于等于(r_{dep+1}),不然不满足递减性,假设第(dep)层就是最后一层,那么由圆柱体体积公式(pi R^2H=pi (n-v))可以得到半径(R)显然需要满足(R leq sqrt{n-v}),这就是半径的枚举范围。

    对于高度,同样右边界不能大于等于(h_{dep+1}),不然不满足递减性,由于已知(R),那么我们也可以用体积公式得到高度(H)显然需要满足(H leq frac{n-v}{R^2}),这就是高度的枚举范围。

    • 搜索顺序剪枝:对于以上推导出的枚举范围,从大到小枚举。

    • 可行性及最优性剪枝:预处理从上向下(i)层的最小侧面积及体积,即第(i)层半径高度都取(i),最小体积(v_i=i^3),最小表面积(s_i=2*i^2)。如果体积(v)加上剩下(dep+1)(1)的最小体积和大于(n)时,可以剪枝。如果表面积(s)加上剩下(dep+1)(1)的最小表面积和大于已经搜到的答案时,可以剪枝。

    • 最优性剪枝:当(frac{2(n-v)}{r_{dep}}+s)大于等于已经搜到的答案时,可以剪枝。

    由圆柱体表面积与体积公式可以得到第(1)到第(dep-1)层的体积之和为

    [sum_{i=1}^{dep-1}(h_i*r_i^2) =n-v ]

    侧面积之和为

    [2sum_{i=1}^{dep-1}(h_i*r_i) ]

    那么可以进行如下推导:

    [2sum_{i=1}^{dep-1}(h_i*r_i) \=frac{2}{r_{dep}}sum_{i=1}^{dep-1}(h_i*r_i)*r_{dep} \ = frac{2}{r_{dep}}sum_{i=1}^{dep-1}(h_i*r_i*r_{dep}) \ geq frac{2}{r_{dep}}sum_{i=1}^{dep-1}(h_i*r_i^2) \ =frac{2(n-v)}{r_{dep}}=x ]

    即第(1)到第(dep-1)层的表面积之和大于等于(x),所以如果(s+x)大于等于已经搜到的答案时,也可以剪枝。

    很多时候,(dfs)的剪枝是可以通过如上的缩放方式推导出来的,这就要求我们需要发现题目隐藏的性质,合理利用已知条件进行优化。

    (Code:)

    #include<bits/stdc++.h>
    using namespace std;
    #define mset(name,val) memset(name,val,sizeof name)
    #define filein(str) freopen(str".in","r",stdin)
    #define fileout(str) freopen(str".out","w",stdout)
    const int N=10000+20,M=20+20,INF=0x3f3f3f3f;
    int n,m,h[M],r[M],Minsumv[M],Minsums[M],ans=INF;
    inline void input(void)
    {
    	scanf("%d%d",&n,&m);
    }
    inline void init(void)
    {
    	for(int i=1;i<=m;i++)
    	{
    		Minsumv[i]=Minsumv[i-1]+(i*i*i);
    		Minsums[i]=Minsums[i-1]+(2*i*i);
    	}
    	h[m+1]=r[m+1]=INF;
    }
    inline void dfs(int dep,int s,int v)
    {
    	if(!dep)
    	{
    		if(v==n)ans=min(ans,s);
    		return;
    	}
    	if(2*(n-v)/(r[dep+1])+s>ans)return;
    	if(v+Minsumv[dep]>n)return;
    	if(s+Minsums[dep]>ans)return;
    	for(int R=min((int)sqrt((n-v)*1.0),r[dep+1]-1);R>=dep;R--)
    	{
    		for(int H=min((n-v)/(R*R),h[dep+1]-1);H>=dep;H--)
    		{
    			r[dep]=R;h[dep]=H;
    			if(dep==m)
    				dfs(dep-1,R*R+2*R*H,R*R*H);
    			else dfs(dep-1,s+2*R*H,v+R*R*H);
    		}
    	}
    }
    int main(void)
    {
    	input();
    	init();
    	dfs(m,0,0);
    	printf("%d
    ",ans==INF?0:ans);
    	return 0;
    } 
    

    <后记>

  • 相关阅读:
    Windows API 之 Windows Service
    揭开Socket编程的面纱 (一)
    开发中“错误: 意外地调用了方法或属性访问。” 和 第一行错误 的IE 两个问题( JQ 进行转义字符 , 分页JS 调用 时参数问题。)
    结合MSDN理解windows service 服务安装的三个类。
    VFW基础知识(一些定义性质的。从CSDN中得到的。)
    初次接触WIN FORM,深入事件、委托、方法 ,深入看不到的C#探索。
    C#: +(特性 ) + Attitude C#(类)前面或者(方法)前面 (中括号)定义
    VFW系列教程经典
    依赖注入
    Windows Service:SC 和 InstallUtil 区别
  • 原文地址:https://www.cnblogs.com/Parsnip/p/10610751.html
Copyright © 2020-2023  润新知