HDU 1558 Segment set, 計算幾何+並查集

來源:互聯網
上載者:User

題目連結:

http://acm.hdu.edu.cn/showproblem.php?pid=1558


題目類型: 計算集合 , 並查集


題目:

A segment and all segments which are connected with it compose a segment set. The size of a segment set is the number of segments in it. The problem is to find the size of some
segment set.





Sample Input
110P 1.00 1.00 4.00 2.00P 1.00 -2.00 8.00 4.00Q 1P 2.00 3.00 3.00 1.00Q 1Q 3P 1.00 4.00 8.00 2.00Q 2P 3.00 3.00 6.00 -2.00Q 5
 


Sample Output

12225

題目大意:

輸入幾條線段,線段由座標上的兩點組成, 每一點有x,y,代表x軸和y軸對應的值。 

當輸入為Q  k時, 輸出與第k條直線直接或間接有相連的線段條數。

分析與總結:

這一題是並查集比較基礎的應用, 但是這題的關鍵在於判斷兩條直線是否相交。


斷兩線段是否相交的方法:
     我們分兩步確定兩條線段是否相交:


1. 快速排斥實驗:設以線段 P1P2 為對角線的矩形為R, 設以線段 Q1Q2 為對角線的矩形為T,如果R和T不相交,顯然兩線段不會相交。

2. 跨立實驗:如果兩線段相交,則兩線段必然相互跨立對方。
若P1P2跨立Q1Q2 ,則向量 ( P1 - Q1 ) 和( P2 - Q1 )位於向量( Q2 - Q1 ) 的兩側,即:
(( P1 - Q1 ) × ( Q2 - Q1 )) * (( P2 - Q1 ) × ( Q2 - Q1 )) < 0。(利用了向量叉積)
當 ( P1 - Q1 ) × ( Q2 - Q1 ) = 0 時,說明 ( P1 - Q1 ) 和 ( Q2 - Q1 )共線,但是因為已經通過快速排斥實驗,所以 P1 一定線上段 Q1Q2上;同理,( Q2 - Q1 ) ×(P2 - Q1 ) = 0 說明 P2 一定線上段 Q1Q2上。

所以判斷P1P2跨立Q1Q2的依據是:(( P1 - Q1 ) × ( Q2 - Q1 )) * (( Q2 - Q1 ) × ( P2 - Q1 )) >= 0。

同理判斷Q1Q2跨立P1P2的依據是:(( Q1 - P1 ) × ( P2 - P1 )) * (( P2 - P1 ) × ( Q2 - P1 )) >= 0。


具體情況如所示:(這裡利用了向量叉積來判斷兩個向量是否位於另一向量的兩側)
注意:只有同時滿足以上兩個條件,即相互跨立對方,兩個線段才相交。


          

代碼:

struct Point{    double x,y;}; double direction(Point p0,Point p1,Point p2){    return (p2.x-p0.x)*(p1.y-p0.y)-(p1.x-p0.x)*(p2.y-p0.y);} bool on_segment(Point p0,Point p1,Point p2){    if((min(p0.x,p1.x)<=p2.x && p2.x<=max(p0.x,p1.x)) && (min(p0.y,p1.y)<=p2.y && p2.y<=max(p0.y,p1.y)))        return true;    return false;} bool Segment_intersect(Point p1,Point p2,Point p3,Point p4){    double d1,d2,d3,d4;    d1 = direction(p3,p4,p1);    d2 = direction(p3,p4,p2);    d3 = direction(p1,p2,p3);    d4 = direction(p1,p2,p4);    if(((d1>0 && d2<0)||(d1<0 && d2>0)) && ((d3>0 && d4<0)||(d3<0&&d4>0)))        return true;    else if(d1==0 && on_segment(p3,p4,p1))        return true;    else if(d2==0 && on_segment(p3,p4,p2))        return true;    else if(d3==0 && on_segment(p1,p2,p3))        return true;    else if(d4==0 && on_segment(p1,p2,p4))        return true;    return false;}

知道了怎樣判斷兩線是否相交後, 一切都好辦。

接下來要做的是, 每次添加一條直線是,都進行並查集的Union操作。

如果要知道第k條線所在的集合有幾條線段,只需要判斷並查集中所有以k的跟結點為跟結點的數量有幾個即可

AC代碼:

#include<iostream>#include<cstdio>#include<cmath>#include<cstring>#define N 1005using namespace std;struct Point2D //二維平面點{    double x,y;    Point2D():x(0),y(0){}    Point2D(double x,double y):x(x),y(y){}    double Mod() const {return sqrt(x*x + y*y);}    friend Point2D operator-(const Point2D& lh,const Point2D& rh){        return Point2D(lh.x-rh.x, lh.y-rh.y);    }    friend double operator&(const Point2D& lh,const Point2D& rh){        return lh.x*rh.y - lh.y*rh.x;    }     friend std::istream& operator>>(std::istream& in, Point2D& pt){        in>>pt.x>>pt.y;        return in;    }}; struct Segment2D{    Point2D bgn, end;    Segment2D():bgn(),end(){}    Segment2D(Point2D b,Point2D e):bgn(b),end(e){}    Segment2D(double x1,double y1,double x2,double y2):bgn(x1,y1),end(x2,y2){}    friend std::istream& operator>>(std::istream& in, Segment2D& pt){        in>>pt.bgn>>pt.end;        return in;    }        friend std::ostream& operator<< (std::ostream& out, Segment2D& pt){        out<<pt.bgn.x<<" "<<pt.bgn.y<<" "<<pt.end.x<<" "<<pt.end.y;        return out;    }};bool SegmentIntersect(const Segment2D& u, const Segment2D& v){    //1.快速排斥實驗,不相交返回0    if((max(u.bgn.x,u.end.x)>=min(v.bgn.x,v.end.x))&&       (max(v.bgn.x,v.end.x)>=min(u.bgn.x,u.end.x))&&       (max(u.bgn.y,u.end.y)>=min(v.bgn.y,v.end.y))&&       (max(v.bgn.y,v.end.y)>=min(u.bgn.y,u.end.y)));    else return false;        //2.跨立實驗,u的兩端點在v兩側,並且v的兩端點在u兩側    if((((u.bgn-v.bgn)&(v.end-v.bgn))*((v.end-v.bgn)&(u.end-v.bgn))>=0)&&       (((v.bgn-u.bgn)&(u.end-u.bgn))*((u.end-u.bgn)&(v.end-u.bgn))>=0))        return true;    else return false;}Segment2D arr[N];int Index, dnum[N];int father[N]; void init(){    for(int i=0; i<N; ++i)        father[i]=i, dnum[i] = 1;} int find(int x){    int i, j=x;    while(j!=father[j]) j=father[j];    while(x!=j){        i = father[x];        father[x] = j;        x = i;    }      return j;}void Union(int x, int y){    int a = find(x);    int b = find(y);    if(a!=b)        father[a] = b;}int main(){    freopen("input.txt", "r", stdin);        int T,n,k,cas=1;    char cmd[2];    scanf("%d",&T);    while(T--){            scanf("%d", &n);        memset(dnum, 0, sizeof(dnum));        Index=0;        init();        while(n--){            scanf("%s", cmd);            if(cmd[0]=='P'){                cin >> arr[++Index];                                for(int i=1; i<=Index-1; ++i){                    if(SegmentIntersect(arr[i], arr[Index]))                        Union(i, Index);                    }            }            else if(cmd[0] == 'Q'){                scanf("%d", &k);                int x=find(k);                int cnt=0;                for(int i=1; i<=Index; ++i){                    if(x==find(i))                        ++cnt;                }                cout << cnt << endl;            }        }        if(T) printf("\n");    }    return 0;}   



——      生命的意義,在於賦予它意義。 

                   原創  http://blog.csdn.net/shuangde800  , By
  D_Double



聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.