POJ-1190生日蛋糕(详细题解)深度优先搜索

POJ-1190

题目描述

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外,以上所有数据皆为正整数)

输入

有两行,第一行为N(N <= 10000),表示待制作的蛋糕的体积为Nπ;第二行为M(M <= 20),表示蛋糕的层数为M。

输出

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

例子输入

100
2

例子输出

68

提示

圆柱公式
体积V = πR 2H
侧面积A’ = 2πRH
底面积A = πR 2

在这里插入图片描述

蛋糕的体积: V = N π = π R 1 2 H 1 + π R 2 2 H 2 + . . . + π R n 2 H n V=Nπ=πR_1^2H_1+πR_2^2H_2+...+πR_n^2H_n V=Nπ=πR12H1+πR22H2+...+πRn2Hn

: N = R 1 2 H 1 + R 2 2 H 2 + . . . + R n 2 H n N=R_1^2H_1+R_2^2H_2+...+R_n^2H_n N=R12H1+R22H2+...+Rn2Hn
其中 ( R 1 > R 2 > R 3 > . . . > R n 且 H 1 > H 2 > H 3 > . . . > H n ) (R_1>R_2>R_3>...>R_n且H_1>H_2>H_3>...>H_n) (R1>R2>R3>...>RnH1>H2>H3>...>Hn)

蛋糕的表面积: Q = S π = π R 1 2 + 2 π R 1 H 1 + 2 π R 2 H 2 + . . . + 2 π R n H n Q=Sπ=πR_1^2+2πR_1H_1+2πR_2H_2+...+2πR_nH_n Q=Sπ=πR12+2πR1H1+2πR2H2+...+2πRnHn

: S = R 1 2 + 2 R 1 H 1 + 2 R 2 H 2 + . . . + 2 R n H n S=R_1^2+2R_1H_1+2R_2H_2+...+2R_nH_n S=R12+2R1H1+2R2H2+...+2RnHn
其中 ( R 1 > R 2 > R 3 > . . . > R n 且 H 1 > H 2 > H 3 > . . . > H n ) (R_1>R_2>R_3>...>R_n且H_1>H_2>H_3>...>H_n) (R1>R2>R3>...>RnH1>H2>H3>...>Hn)

解题思路:
深度优先搜索:
从底层开始搜索,当底层半径和高度确定时,则搭建剩余层蛋糕所需的最小体积(min_v)和最大体积(max_v)就可以确定了,因此,每当我们搭建完一层时,应该比较一下max_v,min_v和剩余可用体积 V − V i V-V_i VVi)此时有三种情况:
第一种情况、

如果搭建完剩余层所需的最小体积(min_v)比剩余可用体积( V − V i , 其 中 V i V-V_i,其中V_i VVi,Vi表示搭建底层蛋糕已经使用的体积)还要大,则说明题目所固定的体积不够用,此时应该减小底层固定的半径 R i 和 H i R_i和H_i RiHi,直到它们满足第三种情况

第二种情况、

如果搭建完剩余蛋糕层所需最大体积(max_v)比剩余可用体积( V − V i V-V_i VVi)还要小的话,则说用题目所固定的体积用不完,此时应该增大底层固定的半径 R i 和 H i R_i和H_i RiHi,直到它们满足第三种情况

第三种情况、

如果搭建完剩余蛋糕层所需的最小体积(min_v)和最大体积(max_v)满足(min_v < = ( V − V i ) < = <=(V-V_i)<= <=(VVi)<=max_v),则说明从第一层至当前层所搭建的蛋糕层符合题目要求,继续向上层搭建

由题目给定的数值,我们无需考虑π值,则上述的体积可用直接用整数值表示,比如:
当前已用体积 V i = R 1 2 H 1 + R 2 2 H 2 + . . . R i 2 H i V_i=R_1^2H_1+R_2^2H_2+...R_i^2H_i Vi=R12H1+R22H2+...Ri2Hi
当前剩余体积 V − V i = N − ( R 1 2 H 1 + R 2 2 H 2 + . . . R i 2 H i ) = R i + 1 2 H i + 1 + R i + 2 2 H i + 2 + . . . R n 2 H n V-V_i=N-(R_1^2H_1+R_2^2H_2+...R_i^2H_i)=R_{i+1}^2H_{i+1}+R_{i+2}^2H_{i+2}+...R_n^2H_n VVi=N(R12H1+R22H2+...Ri2Hi)=Ri+12Hi+1+Ri+22Hi+2+...Rn2Hn

1.首先确定第一层半径R和高度H的最大值最小值:
总共有M层蛋糕,且每一层的半径和高度都是整数 ( R 1 > R 2 > R 3 > . . . > R n 且 H 1 > H 2 > H 3 > . . . > H n ) (R_1>R_2>R_3>...>R_n且H_1>H_2>H_3>...>H_n) (R1>R2>R3>...>RnH1>H2>H3>...>Hn),则最高层的半径和高度至少为1,由此可以确定第一层的半径和高度皆不小于M;

N = R 1 2 H 1 + R 2 2 H 2 + . . . + R n 2 H n N=R_1^2H_1+R_2^2H_2+...+R_n^2H_n N=R12H1+R22H2+...+Rn2Hn 得:

M = 1 , H 1 = 1 M=1,H_1=1 M=1,H1=1时, R 1 R_1 R1最大,且此时 R 1 = N R_1=\sqrt{N} R1=N ,所以 R 1 R_1 R1的取值范围为[M, N \sqrt{N} N ] 当 R 1 R_1 R1的值确定时, H 1 = N R 1 2 H_1=\frac{N}{R_1^2} H1=R12N,同时 H 1 > = M H_1>=M H1>=M

2.依次确定上一层的半径和高的范围

M − i + 1 < = R i < = R i − 1 − 1 ; M-i+1<=R_i<=R_{i-1}-1; Mi+1<=Ri<=Ri11;
M − i + 1 < = H i < = H i − 1 − 1 ; M-i+1<=H_i<=H_{i-1}-1; Mi+1<=Hi<=Hi11;

3.根据已确定的半径和高度判断是否有可能满足题目要求
即分析上述的三种情况,max_v 和 min_v 和 V 三者之间的关系,若不满足则不断改变当前 R 和 H 的值,直到满足为止

4.确定最小的表面积
1.将第一次符合题目要求1的表面积(min_s)记录,然后在遍历其它情况,如果在深度优先搜索的过程中发现已搭建的蛋糕表面积大于之前记录的最小表面积(min_s)则这种情况无需继续遍历,此时应该改变R和H的值,再进行另一种情况的遍历。
2.如果确立一种新的情况则判断当前蛋糕变面积和之前确立的最小表面积的值进行比较,如果当前 S < S< S<min_s,则交换它们的值,(即更新最小表面积)

5.当枚举完所有情况时返回最小表面积的值

代码如下(C++):

//time:563MS
#include<iostream>
#include<cmath>
#include<string>
#include<iomanip>
#include<map>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
int N, M;//分别表示体积和层数
int min_s = 0x7fffffff;//初始化为最大值
inline int min_v(int m)//此函数用于计算搭建剩余蛋糕层所需的最小体积
{
	int v = 0;
	//将剩余层的半径设为最小值,即从最高层到当前的上一层分别为1,2,3,4...M-m
	//每一层高度H取值和R一样,才能保证V最小;
	for (int R = M - m; R >= 1; R--)
	{
		int H = R;
		v += R * R * H;
	}
	return v;
}

inline int max_v(int r, int h, int m)//此函数用于计算搭建剩余蛋糕层所需的最大体积
{
	int v = 0;
	//由于上层半径和高度必须必下层半径和高度小,所以每一层半径和高度的最大取值应是其下层的半径或高度减一
	for (int R = r, H = h; m <= M; R--, H--,m++)
	{
		//只有将剩余的每一层的半径和高度设为最大值,才能保证求得的V最大
		v += R * R * H;
	}
	return v;
}
inline void dfs(int r, int h, int m, int s, int v)//深度优先搜索,其中m表示当前在搭建的层数,s表示已经搭建的蛋糕层的表面积,v表示已经使用的体积
{
	if (m == M && N == v && s < min_s)
	{
		min_s = s;//更新最小表面积的数值
		return;
	}
	//如果预估蛋糕的最小面积大于前面所确立最小面积,则无需继续深索
	if (r == 0 || s + 2 * (N - v) / r > min_s)
		return;

	//如果预估搭建剩余层所需的最小体积大于剩余可用体积,则说明规定的体积不够用,返回
	if (min_v(m) > N - v)//第一种情况
		return;

	//如果预估搭建剩余层所需的最大体积小于剩余可用体积,则说明无法达到规定的体积,返回
	if (max_v(r, h, m) < N - v)//第二种情况
		return;

	//如果上述条件均不满足,则说明满足第三种情况,则继续深索
	for (int R = r - 1; R >= M - m; R--)//枚举下一层蛋糕的半径
		for (int H = h - 1; H >= M - m; H--)//枚举下一层蛋糕的高
		{
		//注意,此时的最小值不是M-m+1,因为对应的是下一层的半径和高,所以最小值应该是M-m
			dfs(R, H, m + 1, s + 2 * R * H, v + R * R * H);//深搜下一层
		}
}
int main()
{
	scanf_s("%d%d", &N, &M);
	for (int r1 = M; r1 * r1 <= N; r1++)//枚举所有可能的半径和高度
		for (int h1 = N / (r1 * r1); h1 >= M; h1--)
		{
			int s = r1 * r1 + r1 * h1 * 2;//表示第一层的表面积
			int v = r1 * r1 * h1;//表示第一层使用的体积
			dfs(r1, h1, 1, s, v);//固定了第一层的半径和高度之后,往上层去探索
		}
	if (min_s == 0x7fffffff)
		cout << 0 << endl;
	else
		cout << min_s << endl;
	return 0;
}

如果此文能够帮助到您,烦请点赞收藏,嘻嘻!
以上内容如有不足,请谅解并欢迎指出,最后附上本人微信公众号,欢迎大家一起讨论学习,更多精彩内容请关注公众号:“艺千秋录”,欢迎留言共同进步;
在这里插入图片描述


  1. 符合题目要求的意思就是成功搭建了层数为M且体积为Nπ的蛋糕。 ↩︎

  • 13
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

艺千秋录

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值