洛谷P1731生日蛋糕 Java解法

题目出处
在这里插入图片描述

说明:以下文字中把最上层当作第1层,最下层当作第M层,和代码一致

思路:首先明确要对什么东西搜索,很明显题目告诉我们了,要对R和H搜,顺便记一下每一次的层数、体积、面积 只要层数到达最后一层记录面积就完事了
比较容易想到的剪枝:1、面积大于min,return 2、体积大于n,return
但是仅仅这么两个简单的剪枝只有30分。后来经过观察发现最上面一层最小体积为1*1*1,
第二层及以上最小体积为2*2*2+1*1*1。
因此我们有了新的剪枝:①已有体积+此层及以上最小体积大于n,return
既然体积可以剪枝,那我们面积肯定也行啊!所以又发现面积剪枝:2*(n-已有体积)/上一层的半径r代表在现有体积的条件下,余下层数最小的侧面积和。在这里,因为r是上一层的半径,但是这一层的半径肯定是小于r的,因此这样算出的侧面积肯定是在现有体积的前提下余下层数最小的侧面积和。
因此我们又有了新的剪枝:②已有面积+2(n-已有体积)/上一层的半径r大于min,return
是否有了这两个剪枝就可以AC了呢,显然不是,我们发现需要对R、H的上下限进行处理,比如R的上限是根号N,H上限就是N,H和R的下限都是其所在层数,上限代码里有解释,这里解释下限:比如最上面一层R最小为1,H最小为1,又因为往下R,H逐层递增且都为正整数,所以第二层R,H最小为2,以此类推,第i层R,H最小值为i。

解释就到这里了,还要提醒一点:函数里不要有太多参数!!!否则极大可能会TLE
(因为第一次我传了7个参数,结果30分都没有,只有10分~~~

代码很详细:

package search;

import java.util.Scanner;

public class P1731 {
	static int N, M, min = Integer.MAX_VALUE, a[];

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		N = sc.nextInt();
		M = sc.nextInt();
		//我们规定最上层为第1层,则最下层为第m层
		a = new int[M + 1];// 存储第i层即上面所有层的体积最小值
		a[0] = 0;
		for (int i = 1; i <= M; i++) {
			a[i] = a[i - 1] + i * i * i;// V=pi*r*r*h,这里略去了pi,r和h每层最小值为i
		}
//		dfs(N+1, N+1, M, 0, 0);
		//在这里我们从最下层开始搜,也就是第m层
		dfs((int)Math.sqrt(N)+1, N+1, M, 0, 0);//pi*r*r*h<=pi*N,h最小值为1,所以r取N开方,+1是为了和dfs函数匹配
		System.out.println(min);
	}

	// dfs
	/**
	 * 
	 * @param h 高度
	 * @param r 半径
	 * @param c 层数
	 * @param v 目前的体积和
	 * @param s 目前的面积和
	 */
	public static void dfs(int r, int h, int c, int v, int s) {//s就是最下层上表面面积+所有层的侧面积
		if (c == 0) {// 蛋糕已完成
			if (v == N) {
				min = Math.min(min, s);
			}
			return;
		}
		if (v + a[c] > N) {// 如果当前体积+所在层最小体积大于N
			return;
		}
		if (s + 2 * (N - v) / r >= min) {//r为上一层的半径,如果当前体积s+之后最小表面积大于等于min
			return;
		}
		for (int i = r-1; i >= c; --i) {//搜半径,上限为下层半径-1,下限为层数
			if (c == M) {//第一层的s加上其上表面面积
				s = i*i;
			}
			for (int j = h-1; j >= c; --j) {//搜高度,,上限为下层高度-1,下限为层数
				dfs(i,j, c-1, v+i*i*j, s+2*i*j);
			}
		}
	}
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值