生日蛋糕(深搜+剪枝)

问题

7 月 17 日是 Mr.W 的生日,ACM-THU 为此要制作一个体积为 NπNπ 的 M 层生日蛋糕,每层都是一个圆柱体。

设从下往上数第 i 层蛋糕是半径为 Ri,高度为 Hi 的圆柱。

当 iRi+1 且 Hi>Hi+1。

由于要在蛋糕上抹奶油,为尽可能节约经费,我们希望蛋糕外表面(最下一层的下底面除外)的面积 QQ 最小。

令 Q=Sπ ,请编程对给出的 N 和 M,找出蛋糕的制作方案(适当的 Ri 和 Hi 的值),使 S 最小。

除 Q 外,以上所有数据皆为正整数。

输入格式

输入包含两行,第一行为整数 N,表示待制作的蛋糕的体积为 Nπ。

第二行为整数 M,表示蛋糕的层数为 M。

输出格式

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

数据范围

1≤N≤10000,
1≤M≤20

输入样例:

100
2

输出样例:

68

思路:

    总面积 = 求每层蛋糕的侧面积+最底层蛋糕的上表面积

    注意:由题干信息可知,本题中可以忽略Pi。
    1.从下往上搜,每次枚举采取块大的先枚举,这样方案数少。

	2.每一层的高度h和半径r,应该如何枚举? 即高度h和半径r的上下界是多少? 
    假设当前体积为v,总体积为n , 剩余体积 n-v
    圆柱体积为 r*r*h 
    若要使 r 尽可能大,则令h=1即可,r <= sqrt(n-v)
    当r确定了后, h = (n-v)/(r*r) [下取整]
    因此,
        R的上界2种情况
        a. Ri>Rj,(i=depth

        同理,H的上界
        H的上界2种情况
        a. Hi>Hj,(i=depth

	3.剪枝:
	    1.如果当前搜索到的最小表面积已经大于了已知的最小表面积直接退出
        2.如果当前搜索到的体积已经超过题目限制则退出
        3.如果由体积推出来当前的表面积已经不优直接退出

参考代码:

#include 
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define LL long long
#define PII pair
#define PIII pair
#define PSI pair
#define PIIS pair >
#define PDD pair
using namespace std;
const int INF=0x3f3f3f3f;
const int N=2e5+5;
const int M=1e7;
const int mod=1e9+7;

int n,m; //体积和高度
int ans; //最优面积
int R[22],H[22]; //每层的半径和高度
int minv[22]; //每一层最小体积
int mins[22]; //每一层最小面积

//s当前面积,cur剩余层数,v当前体积 
void dfs(int s,int cur,int v)
{
    //3种剪枝情况
    //如果当前搜索到的体积已经超过题目限制则退出
	if(v+minv[cur]>n)return ;
    //如果当前搜索到的最小表面积已经大于了已知的最小表面积直接退出
    if(s+mins[cur]>=ans)return;
    //如果由体积推出来当前的表面积已经不优直接退出
    if(s+2*(n-v)/R[cur+1]>=ans)return;


	if(cur==0) //满足条件,更新最优解 
	{
        if(v==n)ans=s;
		return ; 
	} 

    //搜索顺序先R后H,从大到小
    //枚举半径
    for(int i=min(R[cur+1]-1,(int)sqrt(n-v));i>=cur;i--)
    {
        //枚举高度
        for(int j=min(H[cur+1]-1,(n-v)/i/i);j>=cur;j--)
        {
            //记录当前层的R和H
            R[cur]=i;
            H[cur]=j;
            
            int t=0;
            if(cur==m)t=i*i; //加上最底层的表面积
            int cs=2*i*j; //使用了侧面积
            int cv=i*i*j; //使用了体积
            dfs(s+t+cs,cur-1,v+cv);
        }

    }
}

int main()
{
//	io;
	cin>>n>>m;
    //初始化minv和mins,若要使每层面积和高度最小,则取当前层值
    //即 r=h=i
    for(int i=1;i<=m;i++)
    {
        minv[i]=minv[i-1]+i*i*i;
        mins[i]=mins[i-1]+2*i*i;
    }
	ans=INF; 
    //边界值,减少边界情况判断
    R[m+1]=INF,H[m+1]=INF;
	dfs(0,m,0);
    cout<<(ans==INF?0:ans)<<"\n";

    system("pause");
}

你可能感兴趣的:(算法,c++)