BZOJ2618 [Cqoi2006]凸多边形 凸包 计算几何
欢迎访问~原文出处——博客园-zhouzhendong
去博客园看该题解
题目传送门 - BZOJ2618
题意概括
给出多个凸包,求面积交。
题解
首先我们考虑两个凸包相交的情况。
例题:HDU1632
我们可以证明,两个凸包相交,如果面积交为正,那么新构成的面积块一定也是一个凸包。
具体证明可以分情况讨论,反正画几个图就明白了。也可以网上查一查。
那么题目就简单了。
变成了一道水水的码农题。
两个凸包面积交之后,还是凸包,所以,题目就变成了依次进行面积交。
只需要考虑两个凸包相交的情况。
构成的新凸包上的顶点只有可能是两类:
1. 原来两个凸包的某一个的顶点,并且处于另一个凸包内。
2. 原来两个凸包的交点。
那么只需要最暴力的揪出这些点就可以了。
判断一个点是否在里面,可以用面积法。
当然网上有很多其他的方法。
求线段交点,方法就很多了。一搜一大堆的。
代码
#include <cstring> #include <cstdio> #include <cstdlib> #include <algorithm> #include <cmath> using namespace std; const double Eps=1e-8; int Dcmp(double a){ if (fabs(a)<Eps) return 0; return a<0?-1:1; } int Dcmp(double x,double y){ return Dcmp(x-y); } struct Point{ double x,y; Point (){} Point (double x_,double y_){ x=x_,y=y_; } Point operator + (Point a){ return Point(x+a.x,y+a.y); } Point operator - (Point a){ return Point(x-a.x,y-a.y); } Point operator * (double a){ return Point(x*a,y*a); } Point operator / (double a){ return Point(x/a,y/a); } }; double cross(Point a,Point b){ return a.x*b.y-a.y*b.x; } double cross(Point a,Point b,Point c){ return cross(b-a,c-a); } double Dot(Point a,Point b){ return a.x*b.x+a.y*b.y; } double dis(Point a,Point b){ return sqrt(Dot(a-b,a-b)); } Point readPoint(){ double x,y; scanf("%lf%lf",&x,&y); return Point(x,y); } struct Line{ Point a,b; Line (){} Line (Point x,Point y){ a=x,b=y; } }; bool Lcross(Line L1,Line L2){ return Dcmp(cross(L1.a,L2.a,L2.b))*Dcmp(cross(L1.b,L2.a,L2.b))<0; } bool crossed(Line L1,Line L2){ return Lcross(L1,L2)&&Lcross(L2,L1); } Point Cross_Point(Line a,Line b){ Point P=a.a,Q=b.a,v=a.b-a.a,w=b.b-b.a,u=P-Q; double t=cross(w,u)/cross(v,w); return P+v*t; } Point O; bool cmpAngle(Point a,Point b){ double cr=cross(O,a,b); if (Dcmp(cr)==0) return dis(O,a)<dis(O,b); return cr>0; } struct Ploygon{ static const int M=600; int m; Point P[M]; double Area; void clear(){ m=0; Area=-1; } void add(Point x){ for (int i=1;i<=m;i++) if (Dcmp(x.x,P[i].x)==0&&Dcmp(x.y,P[i].y)==0) return; P[++m]=x; } bool cmpO(Point a,Point b){ if (Dcmp(a.y,b.y)==0) return Dcmp(a.x,b.x)<=0; return Dcmp(a.y,b.y)<0; } void buildPloygon(){ int st[M],top=0; O=P[1]; for (int i=2;i<=m;i++) if (!cmpO(O,P[i])) O=P[i]; sort(P+1,P+m+1,cmpAngle); st[++top]=1,st[++top]=2; for (int i=3;i<=m;i++){ while (top>=2&&Dcmp(cross(P[st[top-1]],P[st[top]],P[i]))<0) top--; st[++top]=i; } for (int i=1;i<=top;i++) P[i]=P[st[i]]; m=top; } double area(){ if (Dcmp(Area,-1)!=0) return Area; Area=0; for (int i=2;i<m;i++) Area+=fabs(cross(P[1],P[i],P[i+1])); Area/=2; return Area; } bool inside(Point x){ double ar=0; for (int i=1;i<=m;i++) ar+=fabs(cross(x,P[i],P[i%m+1])); ar/=2; return Dcmp(area(),ar)==0; } }P1,P2,P3; int n,m; int main(){ scanf("%d",&n); for (int cnt=1;cnt<=n;cnt++){ P2.clear(); scanf("%d",&m); while (m--) P2.add(readPoint()); P2.buildPloygon(); if (cnt==1){ P1=P2; continue; } P3.clear(); for (int i=1;i<=P1.m;i++) if (P2.inside(P1.P[i])) P3.add(P1.P[i]); for (int i=1;i<=P2.m;i++) if (P1.inside(P2.P[i])) P3.add(P2.P[i]); for (int i=1;i<=P1.m;i++) for (int j=1;j<=P2.m;j++){ Line L1=Line(P1.P[i],P1.P[i%P1.m+1]),L2=Line(P2.P[j],P2.P[j%P2.m+1]); if (crossed(L1,L2)) P3.add(Cross_Point(L1,L2)); } P3.buildPloygon(); if (Dcmp(P3.area())==0){ printf("0.000"); return 0; } P1=P3; } printf("%.3lf",P1.area()); return 0; }