Segment set(HDU1558)(C)

本文介绍了一个结合并查集与线段相交判断的算法问题,通过实例详细解析了如何判断两条线段是否相交,并利用并查集解决线段连通性问题。

どこでもドア:http://acm.hdu.edu.cn/showproblem.php?pid=1558
P:输入一条线段
Q:输出第k条线段有几个“朋友”(有几条连通的线段)
比较麻烦的是 判断两条线段是否相交。除去判断,这就是一个简单并查集题。
判断两条线段是否相交有好多算法。。我就copy了一个(copy的第一个有bug坑了我半天,所以copy的先验证)

CODE:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<string>
#include<sstream>
#include<set>
#include<cstdlib>
#include<map>
#include<queue>
#include<fstream>
using namespace std;
typedef long long LL;
//const int INF = 0x3f3f3f3f;
const int M = 10005;
int t,n,sum,ma[M],fri[M];
struct EDG    //用来记录边
{
    double x1,y1,x2,y2;
}E[M];
/////判断两线段是否相交(来自copy)
///------------alg 2------------
//叉积
struct Point
{
    double x, y;
};

double mult(Point a, Point b, Point c)
{
    return (a.x-c.x)*(b.y-c.y)-(b.x-c.x)*(a.y-c.y);
}

//aa, bb为一条线段两端点 cc, dd为另一条线段的两端点 相交返回true, 不相交返回false
bool intersect(Point aa, Point bb, Point cc, Point dd)
{
    if ( max(aa.x, bb.x)<min(cc.x, dd.x) )
    {
        return false;
    }
    if ( max(aa.y, bb.y)<min(cc.y, dd.y) )
    {
        return false;
    }
    if ( max(cc.x, dd.x)<min(aa.x, bb.x) )
    {
        return false;
    }
    if ( max(cc.y, dd.y)<min(aa.y, bb.y) )
    {
        return false;
    }
    if ( mult(cc, bb, aa)*mult(bb, dd, aa)<0 )
    {
        return false;
    }
    if ( mult(aa, dd, cc)*mult(dd, bb, cc)<0 )
    {
        return false;
    }
    return true;
}
///------------alg 2------------
/////
int find(int a){//找查结点所在哪个根结点
    return a==ma[a]? a:ma[a]=find(ma[a]);
}
void mix(int a,int b){
    a=find(a); b=find(b);
    if(a!=b)
    {
        ma[a]=b;   //用后来的结点做为根
        fri[b]+=fri[a];   //记录根的朋友数
    }
}

int main()
{
    char c;
    cin>>t;
    while(t--)
    {
        cin>>n;
        sum=1;
        while(n--)
        {
            cin>>c;
            if(c=='P'){
                scanf("%lf%lf%lf%lf",&E[sum].x1,&E[sum].y1,&E[sum].x2,&E[sum].y2);
                ma[sum]=sum;fri[sum]=1;//初始化集合
                for(int i=1;i<sum;i++)
                {
                    Point p1,p2,p3,p4;//仅仅为了把边的点提取出来好用copy来的的判断函数
                    p1.x=E[i].x1;p1.y=E[i].y1;
                    p2.x=E[i].x2;p2.y=E[i].y2;
                    p3.x=E[sum].x1;p3.y=E[sum].y1;
                    p4.x=E[sum].x2;p4.y=E[sum].y2;

                    if(intersect(p1,p2,p3,p4))
                        mix(ma[sum],ma[i]);
                }
                sum++;
            }
            if(c=='Q'){
                int k;
                cin>>k;
                cout<<fri[find(k)]<<endl;
            }
        }
        //格式
        if(t)
            cout<<endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值