题目描述
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 = πR2H
侧面积A’ = 2πRH
底面积A = πR2
分析:这道题,一开始看着,这题放在搜索专题里,这怎么搜索,不好下手,细看题目条件
当i < M时,要求Ri > Ri+1且Hi > Hi+1。 (除Q外,以上所有数据皆为正整数),这两句话比较关键,而且给出了N <= 10000,结合V=pi*R*R*H,可以知道,r最大为100,h最大为10000,那么可以从下往上搜索,DFS,处理好了剪枝就无敌了,没处理好,一直TLE。
这道题的剪枝比较多,开始没有想到将体积和面积的公式联系起来
V=pi*r*r*h,A’=2*pi*r*h; A’=2*V/r;
求和公式:
1^3+2^3+……+n^3=(n(n-1)/2)^2
直接用此公式比下面的循环打表快一点 (MINV[]数组)
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
int MINS,n,m,MINV[22];
#define pow(x) ((x)*(x))
#define sum3_V(x) ((pow((x))*pow((x)+1))>>2)
void dfs(int level,int leftV,int Ri,int Hi,int S)
{
if(level==0&&leftV==0)///蛋糕刚好用完
{
MINS=min(MINS,S);
return ;
}
if(leftV<=0||level<=0) return ;///剪枝,不符合实际的情况
if(S>=MINS)///剪枝,当前面积大于最优解
return ;
if(leftV<sum3_V(level-1))///剪枝,剩余的蛋糕不够用,去掉这个判断不会超时
return ;
for(int i=Ri-1; i>=level; i--)
{
int H=min(Hi-1,(leftV-sum3_V(level-1))/(i*i));
///H为当前能取的高度的上界,leftV为当前剩余的体积,MIN[level-1]为上面所有的体积之和
///根据V=pi*r*r*h,和A=pi*r*r的关系得到
for(int j=H; j>=level; j--)
{
int curV=i*i*j;
int curA=2*i*j;
if(2*leftV/Ri+S>=MINS) continue;///未用完的蛋糕所构成的面积大于最优解,强力剪枝
if(level==m) curA+=i*i;///当前层是最下面的一层
dfs(level-1,leftV-curV,i,j,S+curA);
}
}
}
int main()
{
//freopen("E:\\in.txt","r",stdin);
/*memset(MINV,0,sizeof(MINV));
for(int i=1; i<22; i++)
MINV[i]=MINV[i-1]+i*i*i;*/
while(~scanf("%d%d",&n,&m))
{
MINS=INF;
dfs(m,n,100,10000,0);
if(MINS==INF)
puts("0");
else
printf("%d\n", MINS);
}
return 0;
}
感受:像这个题,乍一看,觉得不是搜索,但是,还是要仔细的读题,分析题目给出的约束条件(例如范围之类),其实还是有一定的规律可循,剪枝,这个就得多分析,从题目意思入手,联系题目中各个数据的关系,本题中的体积,面积,和高度之间的关系恰恰是比较强力的剪枝
另外,这道题学校oj的数据和poj的不一样,应该是学校加强了数据,然后看的别人的题解报告,写得代码WA掉了,感觉写出来没什么区别吧
下面的这份代码中少了一个h的取上界,就在学校oj超限了,poj上可以过,两个oj数据强度不一样
#include<cstdio>
using namespace std;
int ans=0,N,M;
void dfs(int m,int leftv,int sum_S,int last_r,int last_h)
{
if(!m&&!leftv)///蛋糕恰好用完并且是m层
{
if(sum_S<ans||!ans) ans=sum_S;
return;
}
if(m*(last_r-1)*(last_r-1)*(last_h-1)<leftv&&m!=M) return;///剩余体积比最大可能体积大
if(leftv<=0||m<=0||(sum_S>=ans&&ans)) return; ///不符合实际的情况和当前面积大所求的最小面积
if(ans&&sum_S>=ans) return ;///当前面积比最小的大
int r,h,curV,curA;
for(r=last_r-1; r>=m; r--)
for(h=last_h-1; h>=m; h--)
{
curV=r*r*h; ///当前层体积
curA=2*r*h; ///当前层侧面积
if(m==M) ///若为最下面的一层,加上底面积
curA+=r*r;
if(sum_S+2*leftv/r>=ans&&ans)
continue;
dfs(m-1,leftv-curV,sum_S+curA,r,h);
}
}
int main()
{
while(~scanf("%d%d",&N,&M))
{
dfs(M,N,0,100,10000);
printf("%d\n",ans);
}
return 0;
}