深度优先搜索-生日蛋糕
生日蛋糕 (百练1190)
要制作一个体积为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外,以上所有数据皆为正整数)
输入
有两行,第一行为N(N <= 10000),表示待制作的蛋糕的体积为Nπ;第二行为M(M <= 20),
表示蛋糕的层数为M。
输出:仅一行,是一个正整数S(若无解则S = 0)。
输入:
100
2
输出:
68
思路:
蛋糕的体积:V=Nπ=πR21H1+πR22H2+...+πR2nHn
则:N=R21H1+R22H2+...+R2nHn
其中(R1>R2>R3>...>Rn
蛋糕的表面积:Q=Sπ=πR21+2πR1H1+2πR2H2+...+2πRnHn
则:S=R21+2R1H1+2R2H2+...+2RnHn
其中(R1>R2>R3>...>Rn且H1>H2>H3>...>Hn)
当N取最大值10000时,最大半径是当h=1时,即r=100。同理,最大的h=10000
深度优先搜索,枚举什么?
枚举每一层可能的高度和半径。
如何确定搜索范围?
底层蛋糕的最大可能半径和最大可能高度
搜索顺序,哪些地方体现搜索顺序?
从底层往上搭蛋糕,而不是从顶层往下搭
剪枝1:搭建过程中发现已建好的面积已经超过目前求得的最优表面
积,或者预见到搭完后面积一定会超过目前最优表面积,则停止搭建(最优性剪枝)
剪枝2:搭建过程中预见到再往上搭,高度已经无法安排,或者半径
已经无法安排,则停止搭建(可行性剪枝)
剪枝3:搭建过程中发现还没搭的那些层的体积,一定会超过还缺的
体积,则停止搭建(可行性剪枝)
剪枝4:搭建过程中发现还没搭的那些层的体积,最大也到不了还缺
的体积,则停止搭建(可行性剪枝)
假设构建2层蛋糕,最低层为r1,h1,第二层为r2,h2,公式如下:
V=Nπ=πr1平方h1 + πr2平方h2,N=r1平方h1 + r2平方h2
同理:蛋糕表面积=蛋糕最底层蛋糕的圆面积 + 每层蛋糕的侧面积
因为每层蛋糕的顶面积刚好就是最底层蛋糕的圆面积
Q=Sπ= πr1平方 + 2πr1h1 + 2πr2h2,提取π公因子
S = r1平方 + 2r1h1 + 2r2h2
python 代码实现:
1 import math 2 3 # N-蛋糕的总体积,M-层数 4 N, M = 0, 0 5 # 最优表面积,初值为无穷大 6 minArea = float("inf") 7 # 正在搭建中的蛋糕的表面积 8 area = 0 9 # 统计循环的次数 10 num = 0 11 # 当前体积 12 currentVolume = 0 13 14 # 要用m层去凑体积v,最底层半径不能超过r,高度不能超过h 15 # 求出最小表面积放入minArea 16 def Dfs(v, m, r, h): 17 global N, M, minArea, area, num, currentVolume 18 # m=0,v=0时,说明蛋糕搭建成功,因为的递减,m=0说明不需要搭层数了 19 # 而且v=0,说明体积大小也搭建符合题目的要求了,v!=0,说明已经搭到 20 # 顶层蛋糕了,但是体积没有搭到输入N的要求,所以失败了 21 if m == 0: 22 if v == 0: 23 # 找到一个最优值与之前的最优值进行比较,如果更小则替换 24 minArea = min(minArea, area) 25 return 26 else: 27 return 28 # 做m层蛋糕,但是体积v是0或者小于0是做不到的 29 if v <= 0: 30 return 31 # 半径i从最大半径r开始依次递减1,直到m,因为如果要做m层蛋糕, 32 # 最底层的蛋糕的半径最小值为m,同理最底层蛋糕的高度最小值也为m 33 # 因为题目要求是整数,而且逐层半径、高度最小相差1 34 for i in range(r, m-1, -1): 35 # S = r1平方+各层蛋糕的侧面积 36 # 蛋糕的底面积 37 if m == M: 38 area = i * i 39 for j in range(h, m-1, -1): 40 # currentVolume表示搭好的当前蛋糕的体积 41 currentVolume = currentVolume + (i*i*j) 42 # 剪枝:如果按此半径和高度,搭好的蛋糕的体积超过输入的N,则该方案不可行 43 if currentVolume > N: 44 # 深度遍历需要回溯,之前加过这个值了,但是该条路径走不通,因此要把之前加的值减回去 45 currentVolume -= (i * i * j) 46 continue 47 area += 2 * i * j 48 # 剪枝:如果计算出正在搭建的表面积已经大于曾经计算出的最优表面积 49 # 则不需要再往下搭建了,即使能搭建出来也不是最优的表面积 50 if area > minArea: 51 # 该条路径走不通,因此要把之前加的值减回去 52 area -= 2 * i * j 53 continue 54 num += 1 55 Dfs(v-i*i*j, m-1, i-1, j-1) 56 # 走过的路重新往回走,需要把原来增加的值减回去,以保证下次新的走法走到该点时, 57 # area、currentVolume的值是与原初始化的值 58 area -= 2 * i * j 59 currentVolume -= (i * i * j) 60 61 62 def main(): 63 global N, M, minArea, num 64 N, M = map(int, input().split()) 65 # 这里对半径和高度都是做个初略的假设 66 # 最大半径是假设蛋糕就1层,高度是1的情况,根据公式,可以对N开根号即可得最大值 67 # 最大高度是假设蛋糕的半径是1的情况,N就是高度的最大值 68 Dfs(N, M, int(math.sqrt(N)), N) 69 if minArea == float("inf"): 70 print("%d" % 0) 71 else: 72 print("S = %d,循环次数:%d" % (minArea, num)) 73 74 75 if __name__ == '__main__': 76 main()