P1731 [NOI1999]生日蛋糕

真·毒瘤题!


这道题给你那么少的东西,就是要你爆搜了。

先注意一下,不然后面的做不了了,直接WA掉。

他的意思是:

我们希望蛋糕外表面(最下一层的下底面除外)的面积Q最小。

所以除了最下一层的下表面之外的所有蛋糕表面积都要算进答案。

可以发现,上面的圆柱体可以按下去,所以所有的上表面等于最下的圆柱底面积。再加上所有的侧面积就是答案。

千万不要想错了!


翻题解发现:可以使用数组存储我们建这个多边形的半径和高,方便剪枝。

我们必须从下往上搜,因为上面的高度和半径都一定小于下面的。

搜索就是个两个循环嵌套,但是可以优化。

在下面的dfs中,我们只添加侧面积,最后的底面圆柱底面积等结算的时候再加上就可以了。

搜到最上面,必须满足剩下的体积为0,再判断答案可不可以更新。

这就是爆搜思路了。


但是你会赤裸裸地TLE。

你需要剪枝!

  1. 剩下的体积肯定不为0,这不合法,剪枝。

  2. 当前体积 + 当层体积 * 层数 如果还小于总体积,剪枝。

  3. 当前答案 + 1 * 层数 + 底面圆柱底面积 如果还大于等于答案,剪枝。

  4. 刚开始枚举的时候,可以从\(n\)开三次方开始搜,不过为了方便,sqrt即可,问题不大。

  5. 正向枚举半径,逆向枚举高度。原理看讨论区。

  6. 循环中的下界可以不是1,是当前的层数。

加上这些再开\(O_2\)就能过了吧。。。

代码:

#include<cstdio>
#include<cmath>
const int maxn = 30, INF = 0x3f3f3f3f;
int r[maxn], h[maxn];
int n, m, ans = INF;
void dfs(int t, int remain, int res, int layer)
{
	if(remain < 0) return;
	if(res >= ans) return;
	if(t > m + 1) return;
	if(layer == 0 && t == m + 1)
	{
		if(remain == 0 && res + r[1] * r[1] < ans) ans = res + r[1] * r[1];
		return;
	}
	if(remain - r[t - 1] * r[t - 1] * h[t - 1] * layer > 0) return;
	if(res + layer + r[1] * r[1] >= ans) return;
	for(register int i = r[t - 1] - 1; i >= layer; i--)
	{
		for(register int j = h[t - 1] - 1; j >= layer; j--)
		{
			r[t] = i;
			h[t] = j;
			if(remain - i * i * j >= 0) dfs(t + 1, remain - i * i * j, res + 2 * i * j, layer - 1);
			r[t] = 0;
			h[t] = 0;
		}
	}
}
int main()
{
	scanf("%d%d", &n, &m);
	r[0] = h[0] = (int)(sqrt(n));
	//printf("%d\n", ans);
	dfs(1, n, 0, m);
	if(ans == INF) printf("0\n");
	else printf("%d\n", ans);
	return 0;
}
posted @ 2018-08-05 22:03  Garen-Wang  阅读(351)  评论(0编辑  收藏  举报