luogu P1731 [NOI1999]生日蛋糕

本文深入探讨了基于圆柱体积和表面积计算的优化搜索算法,通过枚举半径和高度,结合剪枝策略,实现了对多层圆柱堆积体积和表面积的精确计算与优化。文章详细解释了算法的基本框架、剪枝策略、放缩法估计和记忆化处理,为解决复杂几何堆积问题提供了一种高效的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这里插入图片描述

analysis

搜索

基本框架

显然搜索每一层的h和r即可

关于hi和ri的范围,有如下推导:

根据圆柱体积公式:

Vnow=πr2hV_{now}=\pi r^2hVnow=πr2h

极端情况下,高为1
于是Vnow=πr2V_{now}=\pi r^2Vnow=πr2
按照题意约掉π\piπ得到r=Vnow=N−vnowr=\sqrt{V_{now}}=\sqrt{N-v_{now}}r=Vnow=Nvnow

而题中还有一个条件,就是Ri&lt;Ri+1→Ri&lt;=Ri+1−1R_i&lt;R_{i+1}\rightarrow R_i&lt;=R_{i+1}-1Ri<Ri+1Ri<=Ri+11

于是R∈[layer,min(N−vnow,rlayer+1−1)]R\in[layer,min(\sqrt{N-v_{now}},r_{layer+1}-1)]R[layer,min(Nvnow,rlayer+11)]

当枚举好R了以后,

Vnow=πR2HV_{now}=\pi R^2HVnow=πR2H

H=VnowR2=⌊(N−v)/R2⌋H=\frac{V_{now}}{R^2}=⌊(N-v)/R^2⌋H=R2Vnow=(Nv)/R2

题中同样还有一个条件,就是Hi&lt;Hi+1→Hi&lt;=Hi+1−1H_i&lt;H_{i+1}\rightarrow H_i&lt;=H_{i+1}-1Hi<Hi+1Hi<=Hi+11

于是H∈[layer,min(⌊(N−v)/R2⌋,Hlayer+1−1)]H\in[layer,min(⌊(N-v)/R^2⌋,H_{layer+1}-1)]H[layer,min((Nv)/R2,Hlayer+11)]

剪枝

  • 优化搜索顺序
  1. 定义对于每一层的ri和hi倒序枚举
  • 排除等效冗余
  1. EOF…
  • 可行性剪枝
  1. 鉴于前面用了倒序枚举,一开始枚举的状态可能会过大,因此我们要预算一下最小的体积,如果当前枚举的体积加上最小的后续体积比要求的体积大,那么就不可能在拓展了
  • 最优化剪枝
  1. 借鉴可行性剪枝第一条的思路,预算一下最小的表面积,如果当前表面积加上后续最小表面积大于当前搜出来的最优解的时候就没有必要拓展了
  2. 放缩法估计花费剪枝:

考虑寻找第dep层的h或r和1到dep层的表面积的等或不等关系,从而用第dep层的数据来预判是否最优

第1到dep-1层的表面积可表示为(约掉π\piπ后,不计上表面积):

S=2∗(h[1]∗r[1]+h[2]∗r[2]......+h[dep−1]∗r[dep−1])=2∗∑i−1dep−1h[i]∗r[i]S=2*(h[1]*r[1]+h[2]*r[2]......+h[dep-1]*r[dep-1])=2*\sum_{i-1}^{dep-1}h[i]*r[i]S=2(h[1]r[1]+h[2]r[2]......+h[dep1]r[dep1])=2i1dep1h[i]r[i]

对S进行变形:

S=2∗(h[1]∗r[1]+h[2]∗r[2]......+h[dep−1]∗r[dep−1])S=2*(h[1]*r[1]+h[2]*r[2]......+h[dep-1]*r[dep-1])S=2(h[1]r[1]+h[2]r[2]......+h[dep1]r[dep1])

=2r[dep](h[1]∗r[1]∗r[dep]+h[2]∗r[2]∗r[dep]......+h[dep−1]∗r[dep−1]∗r[dep])=\frac{2}{r[dep]}(h[1]*r[1]*r[dep]+h[2]*r[2]*r[dep]......+h[dep-1]*r[dep-1]*r[dep])=r[dep]2(h[1]r[1]r[dep]+h[2]r[2]r[dep]......+h[dep1]r[dep1]r[dep])

由于r[dep]&gt;=r[i],i∈[1,dep]r[dep]&gt;=r[i],i\in[1,dep]r[dep]>=r[i],i[1,dep]

于是S&gt;=2r[dep](h[1]∗r[1]2+h[2]∗r[2]2......+h[dep−1]∗r[dep−1]2)S&gt;=\frac{2}{r[dep]}(h[1]*r[1]^2+h[2]*r[2]^2......+h[dep-1]*r[dep-1]^2)S>=r[dep]2(h[1]r[1]2+h[2]r[2]2......+h[dep1]r[dep1]2)

由于第1到dep-1层的体积可表示为(约掉π\piπ后):

n−v=h[1]∗r[1]2+h[2]∗r[2]2+......+h[dep−1]∗r[dep−1]2=∑i=1dep−1h[i]∗r[i]2n-v=h[1]*r[1]^2+h[2]*r[2]^2+......+h[dep-1]*r[dep-1]^2=\sum_{i=1}^{dep-1}h[i]*r[i]^2nv=h[1]r[1]2+h[2]r[2]2+......+h[dep1]r[dep1]2=i=1dep1h[i]r[i]2

于是S&gt;=2(n−v)r[dep]S&gt;=\frac{2(n-v)}{r[dep]}S>=r[dep]2(nv)

这个式子意思就是:从1到dep-1层的表面积一定会大于等于2(n−v)r[dep]\frac{2(n-v)}{r[dep]}r[dep]2(nv)

于是当我们搜到第dep层的时候,可以用这个式子来估计未来的最小预算会不会超过当前的最优值,也就是说当2(n−v)r[dep]+snow&gt;bestcost\frac{2(n-v)}{r[dep]}+s_{now}&gt;bestcostr[dep]2(nv)+snow>bestcost的时候剪枝

  • 记忆化
  1. 本题没有记忆化的必要

code

#include <bits/stdc++.h>
using namespace std;
#define loop(i,start,end) for(register int i=start;i<=end;++i)
#define anti_loop(i,start,end) for(register int i=start;i>=end;--i)
#define clean(arry,num) memset(arry,num,sizeof(arry))
#define ll long long
template<typename T>void read(T &x){
	x=0;char r=getchar();T neg=1;
	while(r>'9'||r<'0'){if(r=='-')neg=-1;r=getchar();}
	while(r>='0'&&r<='9'){x=(x<<1)+(x<<3)+r-'0';r=getchar();}
	x*=neg;
}
#define pi 1
#define Val(h,r) (pi*r*r*h)
#define Sur(h,r) (2*pi*r*h)
#define Area(r) (pi*r*r)
#define re register
int n,m;
const int maxn=20000+10;
const int maxm=20;
int minn=INT_MAX;

int minnv[maxm];
int minns[maxm];
int ri[maxm];
int hi[maxm];
void dfs(re int layer,re int nv,re int cost){
	if(cost+Area(ri[m])>minn||2*(n-nv/pi)/ri[layer+1]+cost>minn)
		return;
	if(nv+minnv[layer-1]>n*pi||cost+minns[layer-1]>minn)
		return;
	if(layer<=0){
		if(nv==pi*n)
			minn=min(minn,cost+Area(ri[m]));
		return;
	}
	for(register int r=min(ri[layer+1]-1,(int)sqrt(n-nv/pi));r>=layer;--r){
		for(register int h=min(hi[layer+1]-1,(n-nv/pi)/(r*r));h>=layer;--h){
			ri[layer]=r;
			hi[layer]=h;
			dfs(layer-1,nv+Val(h,r),cost+Sur(h,r));
		}
	}
}


int main(){
	#ifndef ONLINE_JUDGE
	freopen("datain.txt","r",stdin);
	#endif
	
	read(n);
	read(m);
	loop(i,1,m)
		minnv[i]=minnv[i-1]+Val(i,i),
		minns[i]=minns[i-1]+Sur(i,i)+Area(i);
	hi[m+1]=n;
	ri[m+1]=n;
	dfs(m,0,0);
	printf("%d\n",minn/pi);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AndrewMe8211

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值