题意:给出n个点的坐标描述一个多边形画廊。在画廊平面上找到一片表面,从该区域能够看到画廊墙壁上的每一个点;
思路:将这片表面称为多边形的核。核中一点与多边形边界上任意一点的连线都在多边形内部。凸多边形的核为其本身,凹多边形的核为其内部的一部分或不存在;
将多边形的n个顶点转化为n条边的直线方程;逆时针用多边形的边剖分多边形所在平面,保留向里的部分,舍去向外的部分,剩下的即为核;
利用叉积公式计算核面积,即为所求面积;
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using namespace std; const double epsi=1e-10; const int maxn=2010; const double pi=acos(-1.0); inline int sign(const double &x){ if(x>epsi) return 1; if(x<-epsi) return -1; return 0; } struct point{ double x,y; point(double xx=0,double yy=0):x(xx),y(yy){} double operator ^(const point &op2) const{ return x*op2.y-y*op2.x; } }; struct line{ double A,B,C; line(double aa=0,double bb=0,double cc=0):A(aa),B(bb),C(cc){} double f(const point &p) const{ return A*p.x+B*p.y+C; //计算p点代入直线方程后的解 } double rang() const{ return atan2(B,A);//直线的极角 } double d() const{ return C/(sqrt(A*A+B*B));//原点到直线的距离 } point cross(const line &a)const{ double xx=-(C*a.B-a.C*B)/(A*a.B-B*a.A); double yy=-(C*a.A-a.C*A)/(B*a.A-a.B*A); return point(xx,yy); //计算直线与直线a的交点 } }; line b[maxn],SL[maxn]; //当前核边的直线序列b[],多边形的边序列SL[] point c[maxn],d[maxn]; //当前核的顶点序列c[],核的顶点序列d[] int n; double r; point p[maxn]; int t[2]; point plane[2][maxn],q1,q2; int cmp(line a,line b){ //极角作为第一关键字,原点至该直线的距离作为第二关键字比较直线a和直线b的大小 if(sign(a.rang()-b.rang())!=0) return a.rang()<b.rang(); else return a.d()<b.d(); } inline int half_plane_cross(line *a,int n,point *pt){//利用极角计算和返回多边形a内最大凸多边形的顶点序列pt及其长度 sort(a+1,a+n+1,cmp); int tn=1; for(int i=2;i<=n;i++){ //枚举多边形的相邻边,去除极角相同的相邻边或者A=B=0且C>0的边 if(sign(a[i].rang()-a[i-1].rang())!=0) a[++tn]=a[i]; if(sign(a[tn].A)==0&&sign(a[tn].B)==0) if(sign(a[tn].C)==1) tn--;//若C>0则移出a[];否则返回失败标志 else return -1; } n=tn; //a预处理后的长度 int h=0,t=1; //队列的首尾指针初始化 b[0]=a[1]; b[1]=a[2]; c[1]=b[1].cross(b[0]); //直线1和直线2存入a,交点存入c for(int i=3;i<=n;i++){ //枚举直线3到直线n while(h<t&&sign(a[i].f(c[t]))<0) t--; //若队列c非空且c的队尾交点代入直线i后的方程值为负,则队尾元素退出 while(h<t&&sign(a[i].f(c[h+1]))<0) h++;//若队列c非空且c的队首交点代入直线i后的方程值为负,则队首元素退出 b[++t]=a[i]; //直线i进入b的队尾 c[t]=b[t].cross(b[t-1]); //b队尾的两条直线交点进入c队尾 } while(h<t&&sign(b[h].f(c[t]))<0) t--; while(h<t&&sign(b[t].f(c[h+1]))<0) h++; if(h+1>=t) return -1; //若队列空,则失败返回 pt[0]=b[h].cross(b[t]); //b的首尾两条直线的交点作为凸多边形的首顶点 for(int i=h;i<t;i++) pt[i-h+1]=c[i+1]; //凸多边形的其他顶点按c的顺序排列 pt[t-h+1]=pt[0]; //凸多边形首尾相接 return t-h+1; //返回凸多边形的顶点数 } int main() { int x[maxn],y[maxn]; //多边形顶点坐标序列 double ans=0; int n,m; //多边形的顶点数为n,内部最大凸多边形的顶点数为m int test; scanf("%d",&test); while(test--){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d%d",&x[i],&y[i]); x[n+1]=x[1];y[n+1]=y[1]; //首尾相接 for(int i=1;i<=n;i++) //计算每条边的直线方程,SL[]存储直线方程中的A,B,C SL[i]=line(-(y[i]-y[i+1]),-(x[i+1]-x[i]),-(x[i]*y[i+1]-x[i+1]*y[i])); m=half_plane_cross(SL,n,d);//利用极角计算多边形SL内最大凸多边形的顶点数m和顶点序列d ans=0; if(m==-1) printf("0.00\n"); //无凸多边形 else{ for(int i=0;i<m;i++) ans+=d[i]^d[i+1]; //采用叉积的方法计算最大凸多边形的面积 printf("%.2f\n",ans/2); } } return 0; }
时间: 2024-10-07 22:23:16