1 条题解

  • 0
    @ 2025-7-1 18:37:12

    题目大意:

    给你一个由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
    上传者