Loading [MathJax]/jax/output/CommonHTML/autoload/multiline.js

『生日蛋糕 搜索剪枝』

Parsnip·2019-03-28 12:52·299 次阅读

『生日蛋糕 搜索剪枝』

<更新提示>

<第一次更新>


<正文>

#

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#

Copy
100 2

Sample Output#

Copy
68

解析#

预备知识:圆柱体体积公式v=πR2Hv=πR2H,圆柱体侧面积公式s=2πRHs=2πRH,圆柱体底面积公式s=πR2s=πR2

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

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

然后考虑如下的剪枝:

  • 上下界剪枝:枚举R[dep,min(nv,rdep+11)],H[dep,min(nvR2,hdep+11)]R[dep,min(nv,rdep+11)],H[dep,min(nvR2,hdep+11)]

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

对于半径,其枚举右边界显然不能大于等于rdep+1rdep+1,不然不满足递减性,假设第depdep层就是最后一层,那么由圆柱体体积公式πR2H=π(nv)πR2H=π(nv)可以得到半径RR显然需要满足RnvRnv,这就是半径的枚举范围。

对于高度,同样右边界不能大于等于hdep+1hdep+1,不然不满足递减性,由于已知RR,那么我们也可以用体积公式得到高度HH显然需要满足HnvR2HnvR2,这就是高度的枚举范围。

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

  • 可行性及最优性剪枝:预处理从上向下ii层的最小侧面积及体积,即第ii层半径高度都取ii,最小体积vi=i3vi=i3,最小表面积si=2i2si=2i2。如果体积vv加上剩下dep+1dep+111的最小体积和大于nn时,可以剪枝。如果表面积ss加上剩下dep+1dep+111的最小表面积和大于已经搜到的答案时,可以剪枝。

  • 最优性剪枝:当2(nv)rdep+s2(nv)rdep+s大于等于已经搜到的答案时,可以剪枝。

由圆柱体表面积与体积公式可以得到第11到第dep1dep1层的体积之和为

dep1i=1(hir2i)=nvdep1i=1(hir2i)=nv

侧面积之和为

2dep1i=1(hiri)2dep1i=1(hiri)

那么可以进行如下推导:

2dep1i=1(hiri)=2rdepdep1i=1(hiri)rdep=2rdepdep1i=1(hirirdep)2rdepdep1i=1(hir2i)=2(nv)rdep=x

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

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

Code:

Copy
#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\n",ans==INF?0:ans); return 0; }

<后记>

posted @   Parsnip  阅读(299)  评论(0编辑  收藏  举报
编辑推荐:
· 如何在 .NET 中 使用 ANTLR4
· 后端思维之高并发处理方案
· 理解Rust引用及其生命周期标识(下)
· 从二进制到误差:逐行拆解C语言浮点运算中的4008175468544之谜
· .NET制作智能桌面机器人:结合BotSharp智能体框架开发语音交互
阅读排行:
· Cursor预测程序员行业倒计时:CTO应做好50%裁员计划
· 想让你多爱自己一些的开源计时器
· 大模型 Token 究竟是啥:图解大模型Token
· 用99元买的服务器搭一套CI/CD系统
· 当职场成战场:降职、阴谋与一场硬碰硬的抗争
点击右上角即可分享
微信分享提示
目录