#10019. 「一本通 1.3 例 2」生日蛋糕

每日一宏

#define 大法师 dfs

大法师万岁

题目

题目描述

Mr.W 要制作一个体积为 N\pi 的 M 层生日蛋糕,每层都是一个圆柱体。

设从下往上数第 i 蛋糕是半径为 Ri,高度为 Hi 的圆柱。当 i<M 时,要求 Ri>Ri+1 且 Hi >Hi+1。由于要在蛋糕上抹奶油,为尽可能节约经费,我们希望蛋糕外表面(最下一层的下底面除外)的面积 Q 最小。

令 Q =Sπ ,请编程对给出的 N 和 M ,找出蛋糕的制作方案(适当的 Ri 和 Hi 的值),使 S 最小。(除 Q 外,以上所有数据皆为正整数)
在这里插入图片描述

输入格式

第一行为 N ,表示待制作的蛋糕的体积为 Nπ;

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

输出格式

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

样例

输入
100
2
输出
68

思路

一眼大法师
首先我们可以采取简单的思路:
从图中可以看出来,蛋糕的上表面积等于最下面一层的上表面积
所以我们可以先把各层的侧面积求出来,这个结果加上最下面一层的上表面积
但是暴力大法师百分之一百亿过不了,所以要剪枝
根据题目,可以得出以下剪枝:

剪枝①

倒序搜索

剪枝②

先把到第i层的最小体积和最小侧面积求出来
根据题意可得每一层的最小r为1,2,3…i,最小h为1,2,3…i
所以得出到第i层的最小体积为
m i n v [ i ] = m i n v [ i − 1 ] + i ∗ i ∗ i minv[i]=minv[i-1]+i*i*i minv[i]=minv[i1]+iii
最小侧面积为
m i n s [ i ] = m i n s [ i − 1 ] + 2 ∗ i ∗ i mins[i]=mins[i-1]+2*i*i mins[i]=mins[i1]+2ii
如果当前s加上这层以上的最小侧面积比当前得到的最优解还要大
那么就可以return掉了
同样,如果当前v加上这层以上的最小体积比需要制作的蛋糕体积要大
即体积超限,就可以return掉了

剪枝③

在题目中得知

要求 Ri>Ri+1 且 Hi >Hi+1

所以根据体积公式
R 2 H = N − v R^2H=N-v R2H=Nv
得出当前R的范围
R ∈ [ d e p , m i n ( N − v , r [ d e p + 1 ] − 1 ) ] R∈[dep,min(\sqrt{N-v},r[dep+1]-1)] R[dep,min(Nv ,r[dep+1]1)]
以及当前H的范围
H ∈ [ d e p , m i n ( ( N − v ) / R 2 , h [ d e p + 1 ] − 1 ) ] H∈[dep,min((N-v)/R^2,h[dep+1]-1)] H[dep,min((Nv)/R2,h[dep+1]1)]
所以枚举R,H时要用以上两个区间

剪枝④

利用h,r两个数组,1~dep-1层的体积可表示为
Δ s = N − v = ∑ k = 1 d e p − 1 h [ k ] ∗ r [ k ] 2 \Delta s=N-v=\sum_{k=1}^{dep-1}h[k]*r[k]^2 Δs=Nv=k=1dep1h[k]r[k]2
1~dep-1层的侧面积可表示为
Δ v = 2 ∗ ∑ k = 1 d e p − 1 h [ k ] ∗ r [ k ] \Delta v=2*\sum_{k=1}^{dep-1}h[k]*r[k] Δv=2k=1dep1h[k]r[k]
Δ s \Delta s Δs稍微变亿变可得
Δ s = 2 r [ d e p ] ∗ ∑ k = 1 d e p − 1 h [ k ] ∗ r [ k ] ∗ r [ d e p ] > = 2 r [ d e p ] ∗ ∑ k = 1 d e p − 1 h [ k ] ∗ r [ k ] ∗ r [ k ] \Delta s={2\over {r[dep]}}*\sum_{k=1}^{dep-1}h[k]*r[k]*r[dep]>={2\over r[dep]}*\sum_{k=1}^{dep-1}h[k]*r[k]*r[k] Δs=r[dep]2k=1dep1h[k]r[k]r[dep]>=r[dep]2k=1dep1h[k]r[k]r[k]
∑ k = 1 d e p − 1 h [ k ] ∗ r [ k ] ∗ r [ k ] \sum_{k=1}^{dep-1}h[k]*r[k]*r[k] k=1dep1h[k]r[k]r[k]就等于 Δ v \Delta v Δv
所以
Δ s = 2 r [ d e p ] ∗ ∑ k = 1 d e p − 1 h [ k ] ∗ r [ k ] ∗ r [ k ] = 2 r [ d e p ] ∗ Δ v = 2 ∗ ( N − v ) r [ d e p ] > = 2 ∗ ( N − v ) r [ d e p + 1 ] \Delta s={2\over r[dep]}*\sum_{k=1}^{dep-1}h[k]*r[k]*r[k]={2\over r[dep]}*\Delta v={2*(N-v)\over r[dep]}>={2*(N-v)\over r[dep+1]} Δs=r[dep]2k=1dep1h[k]r[k]r[k]=r[dep]2Δv=r[dep]2(Nv)>=r[dep+1]2(Nv)
所以如果当前s加上 Δ s \Delta s Δs大于等于当前得到的最优解那么就不可能是最优解了
因为r[dep]是不确定的,我们又不可能把这个判断塞到循环里
所以要用r[dep+1],根据以上推理可得出 Δ s > = 2 ∗ ( N − v ) r [ d e p + 1 ] \Delta s>={2*(N-v)\over r[dep+1]} Δs>=r[dep+1]2(Nv)
所以这里如果s+ 2 ∗ ( N − v ) r [ d e p + 1 ] {2*(N-v)\over r[dep+1]} r[dep+1]2(Nv)大于等于当前得到的最优解的话
就可以return掉了

代码详解

AC代码

#include<bits/stdc++.h>
using namespace std;
int n,m;
int ms[10005],mv[10005];
int ans=0x3f3f3f;//最大值
void dfs(int,int,int,int,int);
int main()
{
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		ms[i]=ms[i-1]+2*i*i;
		mv[i]=mv[i-1]+i*i*i;
	}
	int rtmp=sqrt(n)+1;
	int htmp=n/(rtmp-1)*(rtmp-1);
	dfs(m,0,0,rtmp,htmp);
	if(ans==0x3f3f3f){
		cout<<-1;
		return 0;
	}
	cout<<ans;
	return 0;
}
void dfs(int dep,int s,int v,int ar,int ah){
	if(!dep){
		if(v==n){
			ans=min(ans,s);
		}
		return;
	}
	if(2*(n-v)/ar+s>=ans){
		return;
	}
	if(s+ms[dep]>ans){
		return;
	}
	if(v+mv[dep]>n){
		return;
	}
	int itmp=min(ar-1,int(sqrt(n-v)));
	for(int i=itmp;i>=dep;i--){
		int jtmp=min((n-v)/(i*i),ah-1);
		for(int j=jtmp;j>=dep;j--){
			int ss=2*i*j;
			int vv=i*i*j;
			if(dep==m){
				dfs(dep-1,s+ss+i*i,v+vv,i,j);
			}
			else{
				dfs(dep-1,s+ss,v+vv,i,j);
			}
		}
	}
}

大法师

void dfs(int dep,int s,int v,int ar,int ah){//dep为当前层数,s和v分别表示当前的侧面积和体积,ar和ah分别表示上一层的半径和高度
	if(!dep){//如果到了0,就证明这m层全找完了
		if(v==n){//如果v到n的话,更新答案
			ans=min(ans,s);
		}
		return;
	}
	if(2*(n-v)/ar+s>=ans){//剪枝④
		return;
	}
	if(s+ms[dep]>ans){//剪枝②
		return;
	}
	if(v+mv[dep]>n){//剪枝②
		return;
	}
	int itmp=min(ar-1,int(sqrt(n-v)));
	for(int i=itmp;i>=dep;i--){//剪枝③ 必须倒着搜,不然会TLE
		int jtmp=min((n-v)/(i*i),ah-1);
		for(int j=jtmp;j>=dep;j--){//剪枝③
			int ss=2*i*j;
			int vv=i*i*j;
			if(dep==m){
				dfs(dep-1,s+ss+i*i,v+vv,i,j);//如果是第m层,把上表面积加上
			}
			else{
				dfs(dep-1,s+ss,v+vv,i,j);
			}
		}
	}
}

初始化

for(int i=1;i<=m;i++){
	ms[i]=ms[i-1]+2*i*i;//计算到第i层的最小侧面积
	mv[i]=mv[i-1]+i*i*i;//计算到第i层的最小体积
}
int rtmp=sqrt(n)+1;
int htmp=n/(rtmp-1)*(rtmp-1);
dfs(m,0,0,rtmp,htmp);//倒着搜
if(ans==0x3f3f3f){
	cout<<-1;
	return 0;
}
cout<<ans;

设置r初始值为sqrt(n)+1,
也就是让第一次的大法师循环有数可取,
因为第m层的半径就是sqrt(n)

小结

剪枝④最重要!一定要写,不然会超时
欢迎大佬暴打本蒟蒻

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值