poj1190
请各位做POJ1190 生日蛋糕(http://poj.org/problem?id=1190)并在线提交,题意如下:
要制作一个体积为Nπ的M层生日蛋糕,每层都是一个圆柱体。设从下往上数第i(1 <= i <= M)层蛋糕是半径为R[i], 高度为H[i]的圆柱。当i < M时,要求R[i]>R[i+1]且H[i]>H[i+1]。由于要在蛋糕上抹奶油,为尽可能节约经费,希望蛋糕外表面(最下一层的下底面除外)的面积Q最小。令Q = Sπ,请编程对给出的N和M,找出蛋糕的制作方案(适当的R[i]和H[i]的值),使S最小,也就是求出蛋糕除了下底面的表面积最小是圆周率的多少倍?除Q外,以上所有数据皆为正整数。输入要求输入有两行,第一行为N(N <= 10000),表示待制作的蛋糕的体积为Nπ;第二行为M(M <= 20),表示蛋糕的层数为M。输出要求输出仅一行,是一个正整数S(若无解则S = 0)。输入输出格式请参考原题页面。要求使用递归-回溯算法,并在算法中尽可能引入尽可能多的剪枝措施。
C++
#include <iostream>
#include <cmath>
#include <algorithm>
#include <functional>
#include <climits>
using namespace std;
class Cake
{
protected:
int N; // 需要构造的蛋糕所有层的体积总和为Nπ
int M; // 需要构造的蛋糕要有M层
int* minA; // minA[i]表示共i层的蛋糕可能的最小侧表面积
int* minV; // minV[i]表示共i层的蛋糕可能的最小体积
int minArea; // 最优值
int area; // 正在搭建的蛋糕已有的表面积
int MaxVForNRH(int n, int r, int h) const; // 计算在蛋糕有n层、底面最大半径为r、最高高度为h的情况下能凑出来的最大体积
void DFS(int v, int top, int r, int h); // 深度优先搜索+剪枝
void Init(); // 初始化
public:
void Solve();
};
// 计算在蛋糕有n层、底面最大半径为r、最高高度为h的情况下能凑出来的最大体积
int Cake::MaxVForNRH(int n, int r, int h) const
{
int v = 0;
for (int i = 0; i < n; i++)
{
v += (r - i) * (r - i) * (h - i);
}
return v;
}
// 深度优先搜索+剪枝,注意搜索半径必须从大到小,否则超时
// v:还需要凑多少体积的蛋糕
// top:还需要搭建多少层
// r:自下而上搭建,即将搭建的这一层的半径可能的最大值
// h:自下而上搭建,即将搭建的这一层的高度可能的最大值
void Cake::DFS(int v, int top, int r, int h)
{
if (top == 0)
{
if (v != 0) // 如果层数为0但体积不为0,不可能
{
return; // 剪枝
}
else
{
minArea = min(minArea, area);
return;
}
}
//还没搭建完所有的层,体积已经超过指定体积了
if (v <= 0)
{
return; // 剪枝
}
// 还没搭建的那些层的最小体积超过了还缺少的体积
if (minV[top] > v)
{
return; // 剪枝
}
// 已搭建好的蛋糕的部分面积加上还没搭建的蛋糕至少需要的表面积,就已经超过了目前求得的最优表面积
if (area + minA[top] >= minArea)
{
return;
}
// 每一层的高度和半径都必须是整数,条件(h < top || r < top)表示再往上搭建蛋糕,高度已经无法安排,或者半径已经无法安排
if (h < top || r < top)
{
return; // 剪枝
}
if (MaxVForNRH(top, r, h) < v) // 剩下还没搭建的那些体积,怎么构造都无法达到还缺少的体积v
{
return; // 剪枝
}
for (int currentR = r; currentR >= top; currentR--) // 枚举底面半径
{
if (top == M)
{
area = currentR * currentR;
}
for (int currentH = h; currentH >= top; currentH--) // 枚举底层高度
{
double flank = 2 * currentR * currentH;
area += flank;
DFS(v - currentR * currentR * currentH, top - 1, currentR - 1, currentH - 1);
area -= flank;
}
}
}
// 初始化
void Cake::Init()
{
area = 0;
minArea = INT_MAX;
minV[0] = 0;
minA[0] = 0;
for (int i = 1; i <= M; i++)
{
minV[i] = minV[i - 1] + i * i * i;
minA[i] = minA[i - 1] + 2 * i * i;
}
}
void Cake::Solve()
{
while (cin >> N >> M)
{
minV = new int[M + 1];
minA = new int[M + 1];
Init();
if (minV[M] > N)
{
cout << 0 << "\n";
}
else
{
int maxH = (N - minV[M - 1]) / (M * M) + 1; // 计算最底层可能的最大高度
int maxR = (int)sqrt(double(N - minV[M - 1]) / M) + 1; // 计算最底层可能的最大半径
DFS(N, M, maxR, maxH);
if (minArea == INT_MAX)
{
cout << 0 << "\n";
}
else
{
cout << minArea << "\n";
}
}
delete[] minA;
delete[] minV;
}
}
int main()
{
Cake obj;
obj.Solve();
return 0;
}
java
import java.util.Scanner;
public class Main_1190 {
static int minv[] = new int[21];// 最小体积
static int mins[] = new int[21];// 最小侧面积
static int M;// 层数
static int N;// 体积N*3.14
static int base;// 最优解
static int area;// 在搭的表面鸡
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
Main_1190 ss = new Main_1190();
for (int i = 1; i <= 20; i++)// 遍历 M 1-20的最小体积和侧面积
{
minv[i] = minv[i - 1] + i * i * i;
mins[i] = mins[i - 1] + 2 * i * i;
}
M = in.nextInt();
N = in.nextInt();
if (minv[M] > N)
System.out.println(0);
else {
base = 10000;
int maxR = (int) Math.sqrt((N - minv[M - 1]) / M) + 1;
int maxH = (N - minv[M - 1] / (M * M) + 1);
ss.dsf(M, N, maxR, maxH);
if (base == 10000)
System.out.println(0);
else
System.out.println(base);
}
}
// n 层最大体积
public int Max(int n, int r, int h) {
int v = 0;
for (int i = 0; i < n; i++) {
v += (h - i) * (r - i) * (r - i);
}
return v;
}
// top:层数v:还需要的体积 r;搭建这层半经 h;搭建这层的高度
public void dsf(int top, int v, int r, int h) {
if (top == 0) {
if (v != 0) {
return;
} else {
base = Math.min(base, area);
return;
}
} else {
if (v <= 0)
return;// 还没有搭完
if (minv[top] > v)
return;// 小于最小体积
if (area + mins[top] > base)
return;// 还有的加现在的大了以前的最好解
if (h < top || r < top)
return;// 搭不到top
if (Max(top, r, h) < v)
return;// 最大小于还有的
for (int R = r; R >= top; R--) {
if (top == M) {
area = R * R;
}
// 地亏调用
for (int H = h; H >= top; H--) {
double f = 2 * R * H;
area += f;
dsf(v - R * R * H, top - 1, R - 1, H - 1);
area -= f;
}
}
}
}
}