P1731 [NOI1999]生日蛋糕

真·毒瘤題!


這道題給你那麼少的東西,就是要你爆搜了。數組

先注意一下,否則後面的作不了了,直接WA掉。優化

他的意思是:spa

咱們但願蛋糕外表面(最下一層的下底面除外)的面積Q最小。code

因此除了最下一層的下表面以外的全部蛋糕表面積都要算進答案。blog

能夠發現,上面的圓柱體能夠按下去,因此全部的上表面等於最下的圓柱底面積。再加上全部的側面積就是答案。rem

千萬不要想錯了!io


翻題解發現:可使用數組存儲咱們建這個多邊形的半徑和高,方便剪枝。class

咱們必須從下往上搜,由於上面的高度和半徑都必定小於下面的。原理

搜索就是個兩個循環嵌套,可是能夠優化。搜索

在下面的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;
}
相關文章
相關標籤/搜索