每日一宏
#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[i−1]+i∗i∗i
最小侧面积为
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[i−1]+2∗i∗i
如果当前s加上这层以上的最小侧面积比当前得到的最优解还要大
那么就可以return掉了
同样,如果当前v加上这层以上的最小体积比需要制作的蛋糕体积要大
即体积超限,就可以return掉了
剪枝③
在题目中得知
要求 Ri>Ri+1 且 Hi >Hi+1
所以根据体积公式
R
2
H
=
N
−
v
R^2H=N-v
R2H=N−v
得出当前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(N−v,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((N−v)/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=N−v=k=1∑dep−1h[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=2∗k=1∑dep−1h[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]2∗k=1∑dep−1h[k]∗r[k]∗r[dep]>=r[dep]2∗k=1∑dep−1h[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=1dep−1h[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]2∗k=1∑dep−1h[k]∗r[k]∗r[k]=r[dep]2∗Δv=r[dep]2∗(N−v)>=r[dep+1]2∗(N−v)
所以如果当前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∗(N−v)
所以这里如果s+
2
∗
(
N
−
v
)
r
[
d
e
p
+
1
]
{2*(N-v)\over r[dep+1]}
r[dep+1]2∗(N−v)大于等于当前得到的最优解的话
就可以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)
小结
剪枝④最重要!一定要写,不然会超时
欢迎大佬暴打本蒟蒻