生日蛋糕
Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 16428 | Accepted: 5836 |
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外,以上所有数据皆为正整数)
设从下往上数第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 = πR 2H
侧面积A' = 2πRH
底面积A = πR 2
体积V = πR 2H
侧面积A' = 2πRH
底面积A = πR 2
/*************************
思路:把所有可能罗列出来,得到最小表面积。做题过程中要考虑从哪里开始,到哪里结束;
也就是说要考虑到初始状态和结束状态,而每次搜索状态的改变都会对应改变一些变量的值,
1.这些变量往往就是搜索时要着重考虑的。
比如加在函数中dfs(int a,int b,int c,int d),
比如每次搜索中sum++或minS = min(minS,nowS);
2.深搜重在一个深字,一条路走到底,所以每次搜索 初始化 和 回溯 也很重要
PS:一开始竟然超时,后来根据高人指点加了剪枝5,神剪枝啊
*************************/
<span style="font-size:14px;">#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <stack>
#include <queue>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
#include <bitset>
#include <list>
using namespace std;
#define INT_MAX 1 << 30
typedef long long ll;
int N,M;
int Vmin[30];
int Smin[30];
int maxR,maxH;
int minV;
int nowV;
int minS;
int nowS;
int maxV(int nn,int rr,int hh)
{
int vv = 0;
for (int i = 0; i < nn; i += 1)
{
vv += (rr-i)*(rr-i)*(hh-i);
}
return vv;
}
void dfs(int v, int m, int maxR, int maxH)
{
if ((m == 0) && (v == 0)) //循环结束条件判断
{
minS = min(minS,nowS);
return ;
}
if ((v <= 0) || (m <= 0)) //剪枝2,其中一值为负则跳出
{
return ;
}
if (nowV >= N) //剪枝3,第一层未搜完便溢出,跳出
{
return ;
}
if (nowS >= minS) //剪枝4,第一层未搜完便溢出,跳出
{
return ;
}
if(maxV(m,maxR,maxH) < v) //剪枝5,不仅考虑溢出,还要考虑不足
{
return ;
}
for (int r = maxR; r >= m; r -= 1) //遍历循环
{
for (int h = maxH; h >= m; h -= 1)
{
if (m == M)
{
nowS = r*r;
nowV = 0;
}
nowS += 2*r*h; //下次循环初始化
nowV += r*r*h;
dfs(v-r*r*h,m-1,r-1,h-1); //循环
nowS -= 2*r*h; //回溯
nowV -= r*r*h;
}
}
}
int main()
{
cin >> N >> M; //输入及存储
Smin[0] = M*M;
Vmin[0] = 0;
minV = 0;
for (int i = 0; i <= M; i += 1)
{
Vmin[i] += i*i*i;
Smin[i] = Smin[i-1] + 2*i*i;
}
if(Vmin[M] > N) //剪枝1
printf("0\n");
else
{
int H = (N-Vmin[M-1])/(M*M) + 1;
int R = (int)sqrt((double)((N-Vmin[M-1])/M))+1;
minS = (1 << 30); //初始化
dfs(N,M,R,H); //搜索
printf("%d\n",minS); //输出
}
return 0;
}</span>
一开始编写的时候在遍历搜索里写错了 dfs,把 maxR, maxH 仍然按照主函数的方法写,但是输出值总是比标准答案小,后来想到是没有考虑到:
m-1 层的半径用这种方式计算可能比m层的半径大。。。那样很可能形成的是一个类似菱形旋转形成的几何体或是什么乱七八糟的东西。。。
因此乖乖在搜索中用 r-1, h-1 进一步搜索。。。