2021-04-26

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;

                }

            }

        }

    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值