天天看點

BZOJ2829: 信用卡凸包【凸包】

2829: 信用卡凸包

我們不需要考慮圓弧,最後加上一個圓的周長就可以了。

然後維護凸包就可以了。

以下是維護了上下凸殼求解的。

#include<cmath>
#include<cstdio>
#include<algorithm>
const int MAXN=10005;
const double pi=acos(-1.0);
int n,m,Topsa,Topsb;double a,b,r,Ans;
struct point{
	double x,y;
	bool operator <(const point b)const{return x==b.x?y<b.y:x<b.x;}
	point operator -(const point b)const{return (point){x-b.x,y-b.y};}
	double operator *(const point b)const{return x*b.y-y*b.x;}
}p[MAXN*4],sa[MAXN*4],sb[MAXN*4];
double GetDist(point x,point y){return sqrt((x.x-y.x)*(x.x-y.x)+(x.y-y.y)*(x.y-y.y));}
int main(){
	freopen("2829.in","r",stdin);
	freopen("2829.out","w",stdout);
	scanf("%d%lf%lf%lf",&n,&a,&b,&r);
	a=a/2-r;b=b/2-r;
	for(int i=1;i<=n;i++){
		double x,y,sita;scanf("%lf%lf%lf",&x,&y,&sita);
		p[++m]=(point){x+b*cos(sita)+a*sin(sita),y+b*sin(sita)-a*cos(sita)};
		p[++m]=(point){x-b*cos(sita)+a*sin(sita),y-b*sin(sita)-a*cos(sita)};
		p[++m]=(point){x+b*cos(sita)-a*sin(sita),y+b*sin(sita)+a*cos(sita)};
		p[++m]=(point){x-b*cos(sita)-a*sin(sita),y-b*sin(sita)+a*cos(sita)};
	}
	std::sort(p+1,p+1+m);
	for(int i=1;i<=m;i++){
		while(Topsa>1&&(p[i]-sa[Topsa])*(sa[Topsa]-sa[Topsa-1])>=0) Topsa--;
		while(Topsb>1&&(p[i]-sb[Topsb])*(sb[Topsb]-sb[Topsb-1])<=0) Topsb--;
		sa[++Topsa]=sb[++Topsb]=p[i];
	}
	for(int i=2;i<=Topsa;i++) Ans+=GetDist(sa[i],sa[i-1]);
	for(int i=2;i<=Topsb;i++) Ans+=GetDist(sb[i],sb[i-1]);
	Ans+=GetDist(sa[1],sb[1])+GetDist(sa[Topsa],sb[Topsb])+pi*2.0*r;
	printf("%.2lf",Ans);
	return 0;
}
           

繼續閱讀