zoukankan      html  css  js  c++  java
  • POJ-1190-生日蛋糕(深搜,剪枝)

    生日蛋糕

    Time Limit: 1000MS Memory Limit: 10000K
    Total Submissions: 23049 Accepted: 8215

    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

    分析

    • 由于要计算表面积(露出来的),那么在一层一层的计算过程中,如果最下面的一层确定了,则上表面积就确定了,所以搜索框架从下到上。
    • 搜索面对的状态有:正在搜索蛋糕第dep层,当前的外表面积,当前的体积,第dep+1层的高度和半径。
    • 在第dep层时,要枚举所有符合要求的高度和半径
      • 半径:(Rin[dep,min(lfloorsqrt{N-v} floor,r[dep+1]-1)])
      • 高度:(Hin[dep,min(lfloor (N-v)/R^2 floor,h[dep+1]-1)])
      • 上面两个右边界的式子可以通过圆柱体积公式 (pi R^2H =pi(N-v))
    • 在上面确定的范围中,使用倒序枚举。R越大,表面积越小
    • 可行性剪枝
      • 可以预处理出从上往下前 (i(1leq ileq M)) 层最小体积和侧面积.显然,当第 1~i 层的半径分别取 1,2,3,... ,i,高度也分别取 1,2,3,... ,i,时,有最小体积与侧面积。
      • 如果当前体积加上1~dep-1 层的最小体积大于N,可以剪枝。
    • 最优性剪枝一,如果当前表面积s加上 1~dep-1 层的最小侧面积大于已经搜到的答案,或者当前体积加上1~dep-1 层的最大体积小于 v,剪枝
    • 最优性剪枝二,通过转换可以得到 (frac{2(n-v)}{r[dep]} + s) 大于已经搜到的答案时,可以剪枝。
    #define MAX 19931117
    int minv[30];
    int mins = MAX;
    int v,m;
    int maxcake (int n, int lr, int lh)
    {
        int ans = 0, i;
        for (i = n, lr--, lh--; i <= m; i++, lr--, lh--)
        {
            ans += lr * lr * lh;
            if (ans >= 10000 || lr * lr * lh >= 10000) return 10000;
        }
        return ans;
    }
    
    void dfs(int n,int lr,int lh,int uv,int us)
    {
    	if(us>=mins)return;
    	if(uv+minv[m-n+1]>v|| uv + maxcake(n, lr, lh) < v)return;
    	int r,h;
    	if(n==m)
    	{
    		for(r = lr-1;r>=1;r--)
    		{
    			if((v-uv)%(r*r))continue;
    			h=(v-uv)/(r*r);
    			if(h>=lh)continue;
    			mins = min(mins,us+2*r*h+(m==1?(r*r):0));
    		}
    		return;
    	}
    	else
    	{
    		for(r = lr-1;r>=m-n+1;r--)
    		{
    			for(h=min(lh - 1, (int)((v - uv) / (r * r))); h >= m - n + 1; h--)
    			{
    				if (n == 1) dfs(2, r, h, r * r * h, r * r + 2 * r * h);
                    else dfs(n + 1, r, h, uv + r * r * h, us + 2 * r * h);
    			}
    		}
    		return ;
    	}
    }
    int main()
    {
    	cin>>v>>m;
    	minv[0] = 0;
    	for(int i = 1;i<=m;i++)
    		minv[i] = minv[i-1]+i*i*i; 
    	dfs(1,101,10001,0,0);
    	if(mins==MAX)
    		cout<<0<<endl;
    	else
    		cout<<mins<<endl;
        return 0;
    }
    
  • 相关阅读:
    03--软件包管理工具 apt
    02--linux操作系统基础学习笔记
    01--vim常用快捷键
    00--Linux常用命令大全
    07 --C语言字符串函数
    06--C语言数学函数
    (备忘)Rect和RectF的区别
    在android程序中加入widget(窗口小部件)并与之交互的关键代码
    (原)android的alertdialog中加入edittext但是不弹出软键盘等问题的解决与原因
    (转)dp和dip是同一个单位
  • 原文地址:https://www.cnblogs.com/1625--H/p/9497827.html
Copyright © 2011-2022 走看看