Description
Input
Output
Sample Input
5 1.5 1.5 2.0 1.0 1.0 2.0 2.0 1.75 2.0 1.0 3.0 0.0 2.0 5 1.5 1.5 2.0 1.0 1.0 2.0 2.0 1.75 2.5 1.0 3.0 0.0 2.0 1
Sample Output
HOLE IS ILL-FORMED PEG WILL NOT FIT
Source
#include<iostream> #include<cstdio> #include<cstring> #include<string> #include<cmath> #include<cstdlib> #include<queue> #include<stack> #include<map> #include<vector> #include<algorithm> using namespace std; #define maxn 10005 #define eps 1e-8 #define max(x,y) (x>y?x:y) #define min(x,y) (x<y?x:y) int Fabs(double d) //重点:精度控制,如果d精度很高,如-0.00000000001即使是小于0,但也当做是0,关系到后面数据处理 { if(fabs(d)<eps) return 0; else return d>0?1:-1; } struct point { double x,y; bool operator == (const point& p) { return Fabs(x-p.x)==0&&Fabs(y-p.y)==0; } }p[maxn]; int n; double pegx,pegy,pegr,max_x,max_y; double x_multi(point p1,point p2,point p3) { return (p2.x-p1.x)*(p3.y-p1.y)-(p3.x-p1.x)*(p2.y-p1.y); } bool point_is_inside() //叉积判断点在凸包内部! 只针对于凸多边形。圆心连接每一条边的端点得到的叉积必须同向。 以此可以延伸出面积法判定点是否在凸包内部。这两种方法都局限于在凸多边形 { point p1; p1.x=pegx,p1.y=pegy; int i,flag=1; double tmp1=0.0,tmp2; for(i=0;i<n;i++) { tmp2=Fabs(x_multi(p1,p[i],p[(i+1)%n])); if(tmp1*tmp2<-eps) { flag=0; break; } tmp1=tmp2; } return flag; } double Len_ab(point p1,point p2) { return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y)); } bool circle_is_inside() //判断圆是否在凸包内 { if(pegr==0.0) return true; int i; double ans; point peg,t; peg.x=pegx,peg.y=pegy; for(i=0;i<n;i++) //判断圆心到多边形的每一条边的最短距离是否不小于半径 { t=peg; t.x+=p[i].y-p[(i+1)%n].y; t.y+=p[(i+1)%n].x-p[i].x; if(Fabs(x_multi(peg,t,p[i])*x_multi(peg,t,p[(i+1)%n]))==1) //如果垂足不在线段上则选择到线段两个端点距离较小的 ans=Fabs(Len_ab(peg,p[i])-Len_ab(peg,p[(i+1)%n]))==-1?Len_ab(peg,p[i]):Len_ab(peg,p[(i+1)%n]); else ans=fabs(x_multi(peg,p[(i+1)%n],p[i]))/Len_ab(p[i],p[(i+1)%n]); // 否则利用面积/底边得到最小距离 if(ans-pegr<-eps) return false; } return true; } int main() { int i,j; while(scanf("%d",&n)&&n>=3) { scanf("%lf%lf%lf",&pegr,&pegx,&pegy); for(i=0;i<n;i++) scanf("%lf%lf",&p[i].x,&p[i].y); double tmp1=0.0,tmp2; bool flag=true; for(i=0;i<n;i++) //判断是否为凸包,即叉积一直是顺时针或一直是逆时针 { tmp2=Fabs(x_multi(p[i],p[(i+1)%n],p[(i+2)%n])); //精度控制,否则一直wrong if(tmp1*tmp2<-eps) { flag=false; break; } tmp1=tmp2; } if(!flag) { puts("HOLE IS ILL-FORMED"); continue; } if(!point_is_inside()||!circle_is_inside()) //判断圆是否在凸多边形内 { puts("PEG WILL NOT FIT"); continue; } puts("PEG WILL FIT"); } return 0; }
bool Onsegment(point p1,point p2,point p3) { double min_x=min(p1.x,p2.x); double min_y=min(p1.y,p2.y); double max_x=max(p1.x,p2.x); double max_y=max(p1.y,p2.y); if(p3.x>=min_x&&p3.x<=max_x&&p3.y>=min_y&&p3.y<=max_y) return true; return false; } bool Is_intersected(point p1,point p2,point p3,point p4) //线段相交 { double d1=x_multi(p1,p2,p3); double d2=x_multi(p1,p2,p4); double d3=x_multi(p3,p4,p1); double d4=x_multi(p3,p4,p2); if(d1*d2<0.0&&d3*d4<0.0) return true; // if(d1==0.0&&Onsegment(p1,p2,p3)) //由于前面的特判,低处的交点不作为计算 // return true; if(d2==0.0&&Onsegment(p1,p2,p4)) return true; if(d3==0.0&&Onsegment(p3,p4,p1)) return true; if(d4==0.0&&Onsegment(p3,p4,p2)) return true; return false; } double Dot(point p1,point p2,point p3) //点积 { return (p2.x-p1.x)*(p3.x-p1.x)+(p2.y-p1.y)*(p3.y-p1.y); } int pointonsegment(point p0,point p1,point p2) //判断点是否在线段上 { return Fabs(x_multi(p0,p1,p2))==0&&Fabs(Dot(p0,p1,p2))<=0; } bool point_is_inside() { int i,num=0; point p1,peg,p2,p3; p1.x=999999999.0,p1.y=pegy,peg.x=pegx,peg.y=pegy; //p1坐为在射线极远处的一个点,可以将射线看做线段 for(i=0;i<n;i++) { if(p[i].y==p[(i+1)%n].y) //如果和多边形的边平行,则判断起点是否在多边形的该边上,避免了和边重合算作无数多个点 { if(pointonsegment(peg,p[i],p[(i+1)%n])) //判断点是否在线段上 return true; } else { p2=p[i],p3=p[(i+1)%n]; if(p2.y>p3.y) //画图知在与多边形端点相交的时候直接计算交的次数都无法直接判定是否在多边形的内外。一条线段的端点有高低之分,此时规定高点的交点为有效交点 swap(p2,p3); if(Is_intersected(peg,p1,p2,p3)) num++; } } return num%2==1; }
double x_multi(point p1,point p2,point p3) { return (p2.x-p1.x)*(p3.y-p1.y)-(p3.x-p1.x)*(p2.y-p1.y); } double Len_ab(point p1,point p2) { return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y)); } double Dot(point p1,point p2,point p3) { return (p2.x-p1.x)*(p3.x-p1.x)+(p2.y-p1.y)*(p3.y-p1.y); } int pointonsegment(point p1,point p2,point p3) { return Fabs(x_multi(p1,p2,p3))==0&&Fabs(Dot(p1,p2,p3))<=0; } bool point_is_inside() { int i; double sum=0.0; for(i=0;i<n;i++) { if(p0==p[i]) //点在多边形端点上 return true; if(p[i]==p[(i+1)%n]) //去重点 continue; if(pointonsegment(p0,p[i],p[(i+1)%n])) //点在多边形边上 return true; double a=Len_ab(p0,p[i]); double b=Len_ab(p0,p[(i+1)%n]); double c=Len_ab(p[i],p[(i+1)%n]); sum+=Fabs(x_multi(p0,p[i],p[(i+1)%n]))*acos((a*a+b*b-c*c)/(2.0*a*b)); //计算角度和,叉积大于0则加上,小于0则减去 } sum=fabs(sum); if(Fabs(sum-2.0*pi)==0) return true; return false; }
int get_tmp(point p0) { return p0.x>=0?(p0.y>=0?0:3):(p0.y>=0?1:2); } bool point_is_inside() { int tmp1,tmp2,sum=0,i; point p0,p1; p0.x=pegx,p0.y=pegy; p1.x=p[0].x-p0.x,p1.y=p[0].y-p0.y; tmp1=get_tmp(p1); for(i=0;i<n;i++) { if(p[i]==p0) break; int t0=Fabs(x_multi(p0,p[i],p[(i+1)%n])); int t1=Fabs((p[i].x-p0.x)*(p[(i+1)%n].x-p0.x)); int t2=Fabs((p[i].y-p0.y)*(p[(i+1)%n].y-p0.y)); if(!t0&&t1<=0&&t2<=0) //被测点在多边形边上 break; p1.x=p[(i+1)%n].x-p0.x,p1.y=p[(i+1)%n].y-p0.y; tmp2=get_tmp(p1); //计算象限 switch((tmp2-tmp1+4)%4) { case 1:{ sum++; break; } case 2: { if(t0>0) sum+=2; else sum-=2; break; } case 3: { sum--; break; } } tmp1=tmp2; } if(i<n||sum) //被测点在多边形边上或者在多边形内部 return true; return false; }