当前位置: 代码迷 >> 综合 >> [poj1190][搜索]生日蛋糕
  详细解决方案

[poj1190][搜索]生日蛋糕

热度:0   发布时间:2023-12-19 05:19:24.0

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

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

Output

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

Sample Input

100 2

Sample Output

68

Hint

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

题解

搜索的奇技淫巧系列
首先表面积一定包含了最底下那个圆柱的圆面积,我们只需要计算侧面积再加上那个圆面积就好
从底往上搜,设最底端圆柱编号为1且编号向上递增,然后开始一通瞎剪
设当前枚举到编号为k的圆柱,下面的体积和为v,侧面积和为s
用r[i],h[i]记录编号为i的圆柱半径及高度
1:对于搜索当前答案>=最小答案直接剪掉这不是废话吗
2:枚举半径的时候从大往小枚举为什么后面再说
3:对于半径的枚举,我们限制范围为 [k,min(sqrt(n?v)),r[k?1]?1] [ k , m i n ( s q r t ( n ? v ) ) , r [ k ? 1 ] ? 1 ]
4:对于高度的枚举,我们限制范围为 [k,min((n?v)/R2,h[k?1]?1)] [ k , m i n ( ( n ? v ) / R 2 , h [ k ? 1 ] ? 1 ) ] ,其中R是枚举的半径
5:推一个柿子
对于第i个圆柱,他和他上面的圆柱体积和一定要为n-v,可替代为 sigma(r[i]2?h[i]) s i g m a ( r [ i ] 2 ? h [ i ] )
同理,表面积和即为 2?sigma(r[i]?h[i]) 2 ? s i g m a ( r [ i ] ? h [ i ] )
处理一下表面积的柿子,可以得到 2/r[k]?sigma(r[i]?r[k]?h[i]) 2 / r [ k ] ? s i g m a ( r [ i ] ? r [ k ] ? h [ i ] )
再处理第一个柿子,可以得到 2/r[k]?sigma(r[i]2?h[i])=(n?v)?2/r[k] 2 / r [ k ] ? s i g m a ( r [ i ] 2 ? h [ i ] ) = ( n ? v ) ? 2 / r [ k ]
由于r[k]一定大于任意一个r[i],所以有
2/r[k]?sigma(r[i]?r[k]?h[i])>=(n?v)?2/r[k] 2 / r [ k ] ? s i g m a ( r [ i ] ? r [ k ] ? h [ i ] ) >= ( n ? v ) ? 2 / r [ k ]
由于第一式代表的实际为第i个圆柱及其上面的圆柱的侧面积和,所以他+s一定要小于最小答案,但是他比较难算出所以不考虑这里剪枝。由于后面那个式子可以快速算出,且他不会大于第一式。那么在满足 (n?v)?2/r[k]+s>=ans ( n ? v ) ? 2 / r [ k ] + s >= a n s 时,第一式也一定满足,于是可以通过这个剪枝
然后你就可以发现,为什么要倒着枚举了

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
int n,m,ans;
int h[11000],r[11000];
int mncos[11000];
void dfs(int k,int s,int v)
{if(k==m+1){if(v==n)ans=min(ans,s);return ;}int u=min(int(double(sqrt(n-v))),r[k-1]-1);for(int i=u;i>=(m-k+1);i--){if(2*(n-v)/i+s>=ans)continue;for(int j=(m-k+1);j<=min((n-v)/(i*i),h[k-1]-1);j++){if(s+2*i*j+mncos[k+1]>=ans)break;if(v+i*i*j>n)break;r[k]=i;h[k]=j;if(k!=1)dfs(k+1,s+2*i*j,v+i*i*j);else dfs(k+1,s+2*i*j+i*i,v+i*i*j);r[k]=h[k]=0;}}
}
int main()
{scanf("%d%d",&n,&m);for(int i=m;i>=1;i--)mncos[i]=mncos[i+1]+2*(m-i+1)*(m-i+1);ans=999999999;r[0]=h[0]=999999999;dfs(1,0,0);if(ans==999999999)printf("0\n");else printf("%d\n",ans);return 0;
}