【校内模拟】帐篷(凸包DP)

简要题意:

有一些要被围起来的点,有一些木桩,请你以这些木桩为顶点构造一个凸多边形,使得要求的点全部在其内部,同时在原点处有一个最高点,请你最小化以你选择的凸多边形为底面的椎体的侧面积。


凸包DP SB题一道。

考场上没看到凸多边形的限制去想凹的情况了。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define db double
#define cs const

using std::cerr;
using std::cout;

template<typename T>
void ckmin(T &a,cs T &b){a>b?a=b:0;}

struct Pnt{
	int x,y;Pnt(){}Pnt(int _x,int _y):x(_x),y(_y){}
	friend Pnt operator+(cs Pnt &a,cs Pnt &b){return Pnt(a.x+b.x,a.y+b.y);}
	friend Pnt operator-(cs Pnt &a,cs Pnt &b){return Pnt(a.x-b.x,a.y-b.y);}
	friend ll operator*(cs Pnt &a,cs Pnt &b){return (ll)a.x*b.y-(ll)b.x*a.y;}
	inline db len()cs{return sqrt((ll)x*x+(ll)y*y);}	
};

inline ll crs(cs Pnt &a,cs Pnt &b,cs Pnt &c){
	return (ll)(b.x-a.x)*(c.y-a.y)-(ll)(c.x-a.x)*(b.y-a.y);
}

cs int N=4e2+7;

int n,m;

ll fib[80],h;

bool ok[N][N];
Pnt p[N],q[N];
int id[N][800],ct[N];

inline ll crs(int a,int b,int c){
	return crs(p[a],p[b],p[c]);
}
inline db dis(int a,int b){return (p[a]-p[b]).len();}

inline db calc(cs Pnt &a,cs Pnt &b){
	db len=(a-b).len();
	if(h==0)return len*.5;
	db d=fabs(a*b)/len/h;
	return len*sqrt(d*d+1)*.5;
}
inline db calc(int a,int b){
	return calc(p[a],p[b]);
}

int ql[N],tl;
int qr[N],tr;
void get_lr(int o,int t){
	int ps;for(ps=2;ps<=ct[t];++ps)
		if(crs(t,o,id[t][ps])==0)break;
	tl=tr=0;ql[++tl]=id[t][ps];
	for(int re i=ps+1;i<ps+ct[t];++i)if(id[t][i]>o){
		if(crs(t,o,id[t][i])>0){
			if(ok[id[t][i]][t])ql[++tl]=id[t][i];
		}else {
			if(ok[t][id[t][i]])qr[++tr]=id[t][i];
		}
	}
}

db f[N][N];
db calc(int o){
	for(int re i=o;i<=n;++i)
		for(int re j=o;j<=n;++j)f[i][j]=1e16;
	bool flag=false;
	for(int re i=o+1;i<=n;++i)
		if(ok[o][i])f[o][i]=calc(o,i),flag=true;
	if(!flag)return 1e16;
	std::vector<int> ps;
	for(int i=o+1;i<=n;++i)ps.push_back(i);
	std::sort(ps.begin(),ps.end(),[o](int a,int b){
		return crs(o,a,b)?crs(o,a,b)>0:dis(o,a)<dis(o,b);});
	for(int t:ps){
		get_lr(o,t);db tmp=1e16;
		for(int re il=1,ir=1;ir<=tr;)
			if(il>tl||crs(t,ql[il],qr[ir])>0)
				f[t][qr[ir]]=tmp+calc(t,qr[ir]),++ir;
			else 
				ckmin(tmp,f[ql[il]][t]),++il;
	}db res=1e16;
	for(int re i=o+1;i<=n;++i)if(ok[i][o])
		for(int re j=o+1;j<=n;++j)
			ckmin(res,f[j][i]+calc(i,o));
	return res;
}

void work(){
	for(int re i=1;i<=n;++i)
		scanf("%d%d",&p[i].x,&p[i].y);
	for(int re i=1;i<=m;++i)
		scanf("%d%d",&q[i].x,&q[i].y);
	std::sort(p+1,p+n+1,[](cs Pnt &a,cs Pnt &b){
		return a.x<b.x||(a.x==b.x&&a.y<b.y);});
	h=n+m>=75?0:fib[n+m];
	for(int re i=1;i<=n;++i)
		for(int re j=1;j<=n;++j){
			bool &flag=ok[i][j]=true;
			for(int re k=1;k<=m;++k)
				flag&=crs(p[i],p[j],q[k])>0;
		}
	for(int re i=1;i<=n;++i){
		int *id=::id[i];
		for(int re j=1;j<=n;++j)id[j]=j;
		id[i]=1;
		std::sort(id+2,id+i+1,[i](int a,int b){
			return crs(i,a,b)?crs(i,a,b)>0:(dis(i,a)<dis(i,b));});
		std::sort(id+i+1,id+n+1,[i](int a,int b){
			return crs(i,a,b)?crs(i,a,b)>0:(dis(i,a)<dis(i,b));});
		int &t=ct[i]=2;
		for(int re j=3;j<=n;++j)
			if(crs(i,id[t],id[j])||(id[t]<i&&id[j]>i))
				id[++t]=id[j];
		for(int re j=t+1;j<t+t;++j)id[j]=id[j-t+1];
	}
	db ans=1e16;
	for(int re i=1;i<=n;++i)
		ans=std::min(ans,calc(i));
	if(ans<1e16)printf("%.3lf\n",ans);
	else puts("NO SOLUTION");
}

void Main(){
	fib[0]=fib[1]=1;
	for(int re i=2;i<=75;++i)
		fib[i]=fib[i-1]+fib[i-2];
	while(~scanf("%d%d",&n,&m))work();
}

inline void file(){
#ifdef zxyoi
	freopen("tent.in","r",stdin);
	cerr<<std::fixed<<std::setprecision(3);
	cout<<std::fixed<<std::setprecision(3); 
#endif
}
signed main(){file();Main();return 0;}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值