1 条题解
-
0
题目大意:
给你一个由n个点构成的多边行,然后一条直线穿过这个多边行,问直线与多边形相交的相邻的点构成线段在多边行内部的长度,即:有多长的线段在多变形内部。
思路,寻找直线与多边形的所有交点,然后判断交点的中点是否在多边形内部,累计求和,
#include<algorithm> #include<cstdio> #include<cstring> #include<cmath> #include<cstdlib> #include<iostream> #include<iomanip> #include<list> #include<map> #include<queue> #include<sstream> #include<stack> #include<string> #include<set> #include<vector> using namespace std; #define PI acos(-1.0) #define EXP exp(1) #define pppp cout<<endl; #define EPS 1e-8 #define LL long long #define ULL unsigned long long //1844674407370955161 #define INT_INF 0x3f3f3f3f //1061109567 #define LL_INF 0x3f3f3f3f3f3f3f3f //4557430888798830399 // ios::sync_with_stdio(false); // 那么cin, 就不能跟C的 scanf,sscanf, getchar, fgets之类的一起使用了。 const int dr[]= {0, 0, -1, 1, -1, -1, 1, 1}; const int dc[]= {-1, 1, 0, 0, -1, 1, -1, 1}; inline int read()//输入外挂 { int ret=0, flag=0; char ch; if((ch=getchar())=='-') flag=1; else if(ch>='0'&&ch<='9') ret = ch - '0'; while((ch=getchar())>='0'&&ch<='9') ret=ret*10+(ch-'0'); return flag ? -ret : ret; } const int maxn=1550; struct Point { double x; double y; Point() {} Point(double x,double y):x(x),y(y) {} Point operator + (const Point &rh) const { return Point(x+rh.x, y+rh.y); } Point operator - (const Point &rh)const { return Point(x-rh.x, y-rh.y); } bool operator == (const Point &rh)const { if(x==rh.x && y==rh.y) return true; return false; } } p[maxn],rec[maxn]; struct Line { Point a; Point b; Line() {} Line(Point a,Point b):a(a),b(b) {} }line; int n,m; double Cross(Point a, Point b)//叉积 { return a.x*b.y-b.x*a.y; // double t=a.x*b.y-b.x*a.y; // if(t>0) // return 1;//锐角 // else if(t<0) // return -1;//钝角 // else // return 0;//平行 } int CheckLineIntersection(Line L1, Line L2)//判断直线的关系,并返回什么关系 { // -1表示直线重合 // 0表示都平行于y轴,没有交点 // 1表示L1平行于y轴 // 2表示L2平行于y轴 // 3表示不平行于y轴且两条直线也不平行或重合 // 4表示不平行于y轴但两条直线平行 int state=0; double KL1,KL2; if( fabs(L1.a.x-L1.b.x)>EPS )//直线L1不平行于y轴没有斜率 { KL1=(L1.b.y-L1.a.y)/(L1.b.x-L1.a.x);//斜率L1 state+=1; } if( fabs(L2.a.x-L2.b.x)>EPS )//直线L1不平行于y轴没有斜率 { KL2=(L2.b.y-L2.a.y)/(L2.b.x-L2.a.x);//斜率L2 state+=2; } if(state==0) { if( fabs(L1.a.x-L2.a.x)<EPS ) return -1;//表示重合 return 0; } else if(state==3) { if( fabs(KL1-KL2)<EPS ) { double Y1=L1.a.y-L1.a.x*KL1;//直线L1的截距 double Y2=L2.a.y-L2.a.x*KL2;//直线L2的截距 if( fabs(Y1-Y2)<EPS ) return -1; return 4; } return 3; } else return state; } Point LineIntersection(Line L1, Line L2, int state)//两条直线的交点,states是两条直线的状态 { switch(state) { case 1://L1存在斜率, L2平行Y轴 { double x=L2.a.x; double KL1=(L1.b.y-L1.a.y)/(L1.b.x-L1.a.x);//斜率L1 double y=(L2.a.x-L1.a.x)*KL1+L1.a.y; return Point(x,y); } case 2://L2存在斜率, L1平行Y轴 { double x=L1.a.x; double KL2=(L2.b.y-L2.a.y)/(L2.b.x-L2.a.x);//斜率L2 double y=(L1.a.x-L2.a.x)*KL2+L2.a.y; return Point(x,y); } case 3://都有斜率 { double KL1=(L1.b.y-L1.a.y)/(L1.b.x-L1.a.x);//斜率L1 double KL2=(L2.b.y-L2.a.y)/(L2.b.x-L2.a.x);//斜率L2 double x=(L1.a.x*KL1-L2.a.x*KL2-L1.a.y+L2.a.y)/(KL1-KL2); double y=KL1*x-KL1*L1.a.x+L1.a.y; return Point(x,y); } } } int CheckLine_SegmentIntersection(Line L, Line S)//直线与线段S的关系,返回什么关系 { // -1表示线段直线重合 // 0表示线段直线平行 // 1表示线段直线相交 // 2表示线段直线不相交 if( fabs(Cross(L.b-L.a, S.b-S.a)) < EPS)//直线与线段平行 { if( fabs(Cross(L.a-S.a, L.b-S.a)) < EPS)//射线与直线重合 return -1; return 0; } else { double tmp = Cross(L.b-L.a, S.a-L.a)*Cross(L.b-L.a, S.b-L.a); if( tmp < 0.0 || fabs(tmp) < EPS ) return 1; return 2; } } Point SegmentIntersection(Line S1, Line S2)//线段与线段的交点,states是两条直线的状态 { int state=CheckLineIntersection(S1,S2); return LineIntersection(S1,S2,state); } bool cmp(Point a,Point b)//将交点从左到右排序,从下到上 { if(fabs(a.x-b.x)<EPS) return a.y<b.y; return a.x<b.x; } Point mid(Point a,Point b)//返回两个点的重点 { return Point((a.x+b.x)/2, (a.y+b.y)/2); } bool on_line(Point a, Point b, Point c)//判断点C在线段ab上 { if (c.x>=min(a.x,b.x) && c.x<=max(a.x,b.x) && c.y>=min(a.y, b.y) && c.y<=max(a.y,b.y))//去掉在线段上的麻烦 { return (abs((c.x-a.x)*(b.y-a.y)-(b.x-a.x)*(c.y-a.y)) <= EPS); } return false; } bool on_flat(Point a)//点是否在多边形内部 { int cnt=0; Point p1=p[0]; Point p2; for(int i=1; i<=n; ++i) { p2=p[i]; if(on_line(p1,p2,a))//表示点在线段上,根据不同要求 { cnt=1; break; } if(a.y>min(p1.y,p2.y))//如果射线交于边的下端点,不算相交,所以是> { if(a.y<=max(p1.y,p2.y)) { if(a.x<=max(p1.x,p2.x)) { if(p1.y!=p2.y)//如果射线和边重合,不算 { //判断是否满足在这个边的左边,其中(p1.x - p2.x) / (p1.y - p2.y)为tan double tem=(a.y-p2.y)*(p1.x-p2.x)/(p1.y-p2.y)+p2.x; if( p1.x==p2.x || a.x<=tem) cnt++; } } } } p1=p2; } if(cnt&1) return true; else return false; } double Dis(Line L)//线段的长度 { return sqrt( (L.a.x-L.b.x)*(L.a.x-L.b.x) + (L.a.y-L.b.y)*(L.a.y-L.b.y)); } double Get_Slove() { int pos=0; for(int i=0; i<n; ++i)//寻找直线与多边形边或者点的交点 { int cnt=CheckLine_SegmentIntersection(line,Line(p[i],p[i+1])); if(cnt==-1)//重合,相交 { //cout<<i<<" "<<i+1<<"线段与直线重合"<<endl; rec[pos++]=p[i]; rec[pos++]=p[i+1]; } else if(cnt==1) { rec[pos++]=SegmentIntersection(line, Line(p[i],p[i+1])); //cout<<i<<" "<<i+1<<"线段与直线相交"<<endl; } } double ret=0; // for(int i=0; i<pos; i++) // printf("%lf %lf\n",rec[i].x,rec[i].y); // printf("*****\n"); sort(rec,rec+pos,cmp); // for(int i=0; i<pos; i++) // printf("%lf %lf\n",rec[i].x,rec[i].y); // printf("~~~~~\n"); for(int i=0; i<pos-1; i++)//计算相交点构成的线段在多边形内部的长度和 { if(on_flat(mid(rec[i],rec[i+1]))) ret+=Dis(Line(rec[i],rec[i+1])); } return ret; } int main() { while(~scanf("%d%d",&n,&m)) { if(!n&&!m) break; for(int i=0; i<n; ++i) scanf("%lf%lf",&p[i].x,&p[i].y); p[n]=p[0]; for(int i=0; i<m; ++i) { scanf("%lf%lf%lf%lf",&line.a.x,&line.a.y,&line.b.x,&line.b.y); printf("%0.3f\n",Get_Slove()); } } return 0; }
- 1
信息
- ID
- 1463
- 时间
- 1000ms
- 内存
- 64MiB
- 难度
- (无)
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者