poj1190 深搜+剪枝

参考:算法竞赛进阶指南–李煜东
搜索框架:从下往上搜索,分支则为每层的半径和高度
搜索面对的状态为:正在搜索蛋糕的第n层,当前外表面面积s,当前体积v,第n+1层的高度和半径
。可以创建数组h和r分别记录每层的高度和半径
整个蛋糕的上表面相加之和即为下表面面积,可以在第m层直接累加到s中,这样在第m-1层往上的搜
索中只用计算表面积即可,减少了计算步骤;
剪枝可以分为5个方面:
1.上下界剪枝
在第n层时,枚举r的范围[n,min(向下取整(根号N-v),r[n+1]-1)]
枚举h的范围[n,min(向下取整((N-v)/r/r),h[n+1]-1)]
这个式子可以由圆柱体积公式πRR*H=π(N-v)推导
2.优化搜索顺序
在上面确定的范围中,使用倒序枚举
3.可行性剪枝
可以预处理出从上往下前i(i>=1&&i<=n)层的最小体积和侧面积。当第i层的半径和高度分别取i时
有最小体积和最小侧面积
4.最优性剪枝一
如果当前表面积加上1~n-1 层的最小侧面积大于已经搜到的答案,放弃
5.最优性剪枝二
利用h与r数组1~n-1层的体积可表示为N-v=∑(k=1,n-1)h[k]*r[k]*r[k],
1~n-1层的表面积可表示为2∑(k=1,n-1)h[k]*r[k];
因为 2∑(k=1,n-1)h[k]*r[k]=2/r[n]*∑(k=1,n-1)h[k]*r[k]*r[n]>=2/r[n]*∑(k=1,n-1)h[k]*r[k]*r[k]>=2(N-v)/r[n] 所以当2(N-v)/r[n] +s大于已经搜到的答案时 可以剪枝

#include<bits/stdc++.h>
using namespace std;
const int inf= 0x3f3f3f3f;
int N,n,m,s=0,mins[30],minv[30],h[30],r[30],ans=inf,v=0;

void dfs(int n)
{
	if(!n){//更新答案 
	
	if(v==N) ans=min(ans,s);
	return ;}
	for(r[n]=min((int )sqrt(N-v),r[n+1]-1);r[n]>=n;r[n]--)
		for(h[n]=min((int)((double)(N-v)/r[n]/r[n]),h[n+1]-1);h[n]>=n;h[n]--)
		{
			if(v+minv[n-1]>N)continue;//剪枝3 
			if(s+mins[n-1]>ans)continue;//剪枝4 
			if((double)2*(N-v)/r[n]+s>ans)continue;//剪枝 5 
			if(n==m) s+=r[n]*r[n];
			v+=r[n]*r[n]*h[n];
			s+=2*r[n]*h[n];
			dfs(n-1);
			if(n==m)s-=r[n]*r[n];//还原现场 
			v-=r[n]*r[n]*h[n];
			s-=2*r[n]*h[n];
		}
}



int main()
{
	cin>>N>>m;
	
	mins[0]=minv[0]=0;
	for(int i=1;i<=m;i++)
	{
		minv[i]=minv[i-1]+i*i*i;//最小体积 
		mins[i]=mins[i-1]+i*i;//最小侧面积 实际应为2*i*i  缩小了范围 使运算更快 
	}
	h[m+1]=r[m+1]=inf;// 
	dfs(m);//倒序 剪枝 2 
	cout<<ans<<endl;
}

猜你喜欢

转载自blog.csdn.net/qq_43768149/article/details/88024783