• HDU 4629 Burning 几何 + 扫描线


     总体思路参考了 这里

     细节:1.控制精度,虽然这题没卡精度,不过还是要控制一下。

    之前 bool operator<( const Point& A, const Point& B ) 函数没有控制精度,导致了相当大的误差。

    2.三点共线的点判掉,虽然不影响结果,但是可以减少点的数目

    3.扫入线扫出线的判断:判断第三个点在那两点所形成的的直线的上方还是下方,上方为扫入线, 下方为扫出线

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <vector>
    #include <cmath>
    #include <algorithm>
    
    using namespace std;
    
    const int MAXN = 20010;
    
    const double eps = 1e-9;
    
    struct Point
    {
        double x, y;
        int type;
        Point( double x = 0, double y = 0, int tp = 0 ): x(x), y(y), type(tp) { }
        void read_Point()
        {
            scanf( "%lf%lf", &x, &y );
            return;
        }
    };
    
    struct Line
    {
        Point a, b;   //线段起点,终点
        int type;     //标记扫入线还是扫出线
        Line() {}
        Line( Point& x, Point& y, int tp ): a(x), b(y), type(tp) {}
    };
    
    int cntX;
    vector<double> Lx;
    vector<Line>   line;
    vector<Point>  qujian[MAXN];
    double ans[160];
    
    typedef Point Vector;
    
    inline int dcmp( double x )    //控制精度
    {
        if ( fabs(x) < eps ) return 0;
        else return x < 0 ? -1 : 1;
    }
    
    Vector operator+( Vector A, Vector B )       //向量加
    {
        return Vector( A.x + B.x, A.y + B.y );
    }
    
    Vector operator-( Vector A, Vector B )       //向量减
    {
        return Vector( A.x - B.x, A.y - B.y );
    }
    
    Vector operator*( Vector A, double p )      //向量数乘
    {
        return Vector( A.x * p, A.y * p );
    }
    
    Vector operator/( Vector A, double p )      //向量数除
    {
        return Vector( A.x / p, A.y / p );
    }
    
    bool operator<( const Point& A, const Point& B )   //两点比较
    {
        return dcmp( A.x - B.x ) < 0 || ( dcmp( A.x - B.x ) == 0 && dcmp( A.y - B.y ) < 0 );
    }
    
    bool operator==( const Point& a, const Point& b )   //两点相等
    {
        return dcmp( a.x - b.x ) == 0 && dcmp( a.y - b.y ) == 0;
    }
    
    double Dot( Vector A, Vector B )    //向量点乘
    {
        return A.x * B.x + A.y * B.y;
    }
    
    double Length( Vector A )           //向量模
    {
        return sqrt( Dot( A, A ) );
    }
    
    double Angle( Vector A, Vector B )    //向量夹角
    {
        return acos( Dot(A, B) / Length(A) / Length(B) );
    }
    
    double Cross( Vector A, Vector B )   //向量叉积
    {
        return A.x * B.y - A.y * B.x;
    }
    
    double Area2( Point A, Point B, Point C )    //向量有向面积
    {
        return Cross( B - A, C - A );
    }
    
    Vector Rotate( Vector A, double rad )    //向量旋转
    {
        return Vector( A.x * cos(rad) - A.y * sin(rad), A.x * sin(rad) + A.y * cos(rad) );
    }
    
    Vector Normal( Vector A )    //向量单位法向量
    {
        double L = Length(A);
        return Vector( -A.y / L, A.x / L );
    }
    
    Point GetLineIntersection( Point P, Vector v, Point Q, Vector w )   //两直线交点
    {
        Vector u = P - Q;
        double t = Cross( w, u ) / Cross( v, w );
        return P + v * t;
    }
    
    double DistanceToLine( Point P, Point A, Point B )    //点到直线的距离
    {
        Vector v1 = B - A, v2 = P - A;
        return fabs( Cross( v1, v2 ) ) / Length(v1);
    }
    
    double DistanceToSegment( Point P, Point A, Point B )   //点到线段的距离
    {
        if ( A == B ) return Length( P - A );
        Vector v1 = B - A, v2 = P - A, v3 = P - B;
        if ( dcmp( Dot(v1, v2) ) < 0 ) return Length(v2);
        else if ( dcmp( Dot(v1, v3) ) > 0 ) return Length(v3);
        else return fabs( Cross( v1, v2 ) ) / Length(v1);
    }
    
    Point GetLineProjection( Point P, Point A, Point B )    // 点在直线上的投影
    {
        Vector v = B - A;
        return A + v*( Dot(v, P - A) / Dot( v, v ) );
    }
    
    bool SegmentProperIntersection( Point a1, Point a2, Point b1, Point b2 )  //线段相交,交点不在端点
    {
        double c1 = Cross( a2 - a1, b1 - a1 ), c2 = Cross( a2 - a1, b2 - a1 ),
                    c3 = Cross( b2 - b1, a1 - b1 ), c4 = Cross( b2 - b1, a2 - b1 );
        return dcmp(c1)*dcmp(c2) < 0 && dcmp(c3) * dcmp(c4) < 0;
    }
    
    /******************以上模板********************/
    
    int N;
    
    void init()
    {
        int len = line.size();
        for ( int i = 0; i < len; ++i )
            for ( int j = i + 1; j < len; ++j )
            {
                //如果线段相交
                if ( SegmentProperIntersection( line[i].a, line[i].b, line[j].a, line[j].b ) )
                {
                    //获得交点
                    Point pp = GetLineIntersection( line[i].a, line[i].b - line[i].a, line[j].a, line[j].b - line[j].a );
                    Lx.push_back( pp.x );
                }
            }
    
        sort( Lx.begin(), Lx.end() );
        cntX = unique( Lx.begin(), Lx.end() ) - Lx.begin();
        return;
    }
    
    void GetAns()
    {
        for ( int i = 0; i <= N; ++i ) ans[i] = 0.0;
    
        for ( int i = 0; i < cntX - 1; ++i )
        {
            int dep = 0;  //覆盖度
            int sz = qujian[i].size();
            double &st = Lx[i], &ed = Lx[i + 1];
            for ( int j = 0; j < sz - 1; ++j )
            {
                dep += qujian[i][j].type;
                ans[dep]+=(ed-st)*(qujian[i][j+1].x-qujian[i][j].x+qujian[i][j+1].y-qujian[i][j].y)/2.0;
            }
        }
    
        for ( int i = 1; i <= N; ++i )
            printf( "%.10lf
    ", ans[i] );
        return;
    }
    
    //void show()
    //{
    //    for ( int i = 0; i < cntX - 1; ++i )
    //    {
    //        int sz = qujian[i].size();
    //        for ( int j = 0; j < sz; ++j )
    //        {
    //            printf( "%.6f %.6f %d
    ", qujian[i][j].x, qujian[i][j].y, qujian[i][j].type );
    //        }
    //        puts("******************
    ");
    //    }
    //}
    
    void solved()
    {
        init();
    
        for ( int i = 0; i < cntX; ++i ) qujian[i].clear();
        int len = line.size();
    
        //遍历每个线段
        for ( int i = 0; i < len; ++i )
        {
            //查询线段左端点在Lx的起始位置
            int j = lower_bound( Lx.begin(), Lx.begin() + cntX, line[i].a.x ) - Lx.begin();
            //遍历线段横跨的区间
            for ( ; j < cntX - 1; ++j )
            {
                double st = Lx[j], ed = Lx[j + 1];
                if ( dcmp( line[i].b.x - ed ) < 0 ) break;
                Point p1 = 
            GetLineIntersection( line[i].a, line[i].b-line[i].a, Point(st,0.0,0), Point(0,1.0,0)); Point p2 =
            GetLineIntersection( line[i].a, line[i].b-line[i].a, Point(ed,0.0,0), Point(0,1.0,0)); qujian[j].push_back( Point( p1.y, p2.y, line[i].type ) ); } } for ( int i = 0; i < cntX - 1; ++i ) sort( qujian[i].begin(), qujian[i].end() ); GetAns(); return; } //两点确定直线一般式 void GetABC( Point a, Point b, double& A, double& B, double& C ) { if( dcmp( a.x - b.x ) == 0 ) B = 0.0; else B = -1.0; A = ( b.y - a.y ) / ( b.x - a.x ); C = a.y - A * a.x; return; } //判断扫入扫出线 int SaoRuSaoChu( Point p1, Point p2, Point c ) { double A, B, C; GetABC( p1, p2, A, B, C ); if ( dcmp( B ) == 0 ) return 0; double res = A * c.x + B * c.y + C; return dcmp( res ) < 0 ? 1 : -1; //扫入线 1, 扫出线 -1 } int main() { //freopen( "1009.in", "r", stdin ); //freopen( "s.txt", "w", stdout ); int T; scanf( "%d", &T ); while ( T-- ) { Lx.clear(); line.clear(); scanf( "%d", &N ); for ( int i = 0; i < N; ++i ) { Point p[3]; for ( int j = 0; j < 3; ++j ) p[j].read_Point(); //判断三点共线 if ( dcmp( Cross( p[1] - p[0], p[2] - p[0] ) == 0 ) ) continue; for ( int j = 0; j < 3; ++j ) { Lx.push_back( p[j].x ); int k = j + 1; if ( k >= 3 ) k = 0; Line tmpL( p[j], p[k], 0 ); //令a点为线段左端点, b为线段右端点 if ( dcmp( tmpL.b.x - tmpL.a.x ) < 0 ) swap( tmpL.a, tmpL.b ); int m = 3 - j - k; //除了构成线段的两点外的那个点 //判断扫入扫出线 int tp = SaoRuSaoChu( p[j], p[k], p[m] ); //如果这条线垂直于x轴,既不是扫入也不是扫出线 if ( tp == 0 ) continue; //得到扫入扫出线 tmpL.type = tp; line.push_back( tmpL ); } } solved(); //printf("lineN = %d cntX=%d ", (int)line.size(), (int)Lx.size() ); //show(); } return 0; }
  • 相关阅读:
    「考试」省选62
    「考试」省选61
    「考试」省选59
    「刷题」THUPC泛做
    「考试」省选58
    「考试」省选57
    「考试」省选56
    「考试」省选55
    「考试」省选54
    「考试」省选52
  • 原文地址:https://www.cnblogs.com/GBRgbr/p/3229561.html
Copyright © 2020-2023  润新知