hdu 1558 Segment set (并查集+判断线段位置关系)

Segment set

Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 3486    Accepted Submission(s): 1297


Problem Description
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.

 

Input
In the first line there is an integer t - the number of test case. For each test case in first line there is an integer n (n<=1000) - the number of commands. 

There are two different commands described in different format shown below:

P x1 y1 x2 y2 - paint a segment whose coordinates of the two endpoints are (x1,y1),(x2,y2).
Q k - query the size of the segment set which contains the k-th segment.

k is between 1 and the number of segments in the moment. There is no segment in the plane at first, so the first command is always a P-command.
 

Output
For each Q-command, output the answer. There is a blank line between test cases.
 

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

Sample Output
  
  
1 2 2 2 5
 

线段相交分为两种情况:一、线段相互交叉,即相互跨立 。二、一条线段的某个端点在另一条线段上。

double cross(point a,point b,point c)      //叉积判断点与直线位置关系
{                                          //若点在直线逆时针方向,返回正值
    return (b.x-a.x)*(c.y-a.y)-(c.x-a.x)*(b.y-a.y); //<span style="font-family: Arial, Helvetica, sans-serif;">若点在直线顺时针方向,返回负值</span>
}                                       //若点在线段上(或延长线)上返回0

bool ifin(point a,point b,point c)   //判断点是否落在线段上
{
    if(c.x>=min(a.x,b.x)&&c.y>=min(a.y,b.y)&&
       c.x<=max(a.x,b.x)&&c.y<=max(a.y,b.y))
        return true;
    return false;
}


则判断线段是否相交,可用下面算法:

bool judge(point a,point b,point c,point d)
{
    //快速排斥实验,不想交返回0
    if(cross(a,b,c)==0&&ifin(a,b,c))
        return true;
    else if(cross(a,b,d)==0&&ifin(a,b,d))
        return true;
    else if(cross(c,d,a)==0&&ifin(c,d,a))
        return true;
    else if(cross(c,d,b)==0&&ifin(c,d,b))
        return true;
    //跨立实验,线段ab的两端点在cd两端,且线段cd的两端点在线段ab两端
    if(cross(a,b,c)*cross(a,b,d)<0&&cross(c,d,a)*cross(c,d,b)<0)
        return true;
    return false;
}

思路:每次得到一条线段判断和前面所有线段是否相交,运用并查集相关操作,得到答案。


#include"stdio.h"
#include"string.h"
#include"math.h"
#include"vector"
#include"iostream"
#include"algorithm"
using namespace std;
#define N 1005
const int inf=0x7fffffff;
int pre[N],num[N];
struct point
{
    double x,y;
};
struct node
{
    point p1,p2;
}f[N];
double cross(point a,point b,point c)      //叉积判断点与直线位置关系
{                                          //若点在直线逆时针方向,返回正值
    return (b.x-a.x)*(c.y-a.y)-(c.x-a.x)*(b.y-a.y);
}
bool ifin(point a,point b,point c)
{
    if(c.x>=min(a.x,b.x)&&c.y>=min(a.y,b.y)&&
       c.x<=max(a.x,b.x)&&c.y<=max(a.y,b.y))
        return true;
    return false;
}
bool judge(point a,point b,point c,point d)
{
    //快速排斥实验,不想交返回0
    if(cross(a,b,c)==0&&ifin(a,b,c))
        return true;
    else if(cross(a,b,d)==0&&ifin(a,b,d))
        return true;
    else if(cross(c,d,a)==0&&ifin(c,d,a))
        return true;
    else if(cross(c,d,b)==0&&ifin(c,d,b))
        return true;
    //跨立实验,线段ab的两端点在cd两端,且线段cd的两端点在线段ab两端
    if(cross(a,b,c)*cross(a,b,d)<0&&cross(c,d,a)*cross(c,d,b)<0)
        return true;
    return false;
}
int findx(int x)
{
    if(x!=pre[x])
        pre[x]=findx(pre[x]);
    return pre[x];
}
void unionset(int a,int b)
{
    int f1,f2;
    f1=findx(a);
    f2=findx(b);
    if(f1!=f2)
    {
        pre[f1]=f2;
        num[f2]+=num[f1];
    }
}
int main()
{
    int T,n,i,j,k,x;
    char ch;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(i=1;i<=n;i++)
        {
            pre[i]=i;
            num[i]=1;
        }
        for(i=1,j=1;i<=n;i++)
        {
            getchar();
            scanf("%c",&ch);
            if(ch=='Q')
            {
                scanf("%d",&x);
                int xx=findx(x);
                printf("%d\n",num[xx]);
            }
            else
            {
                scanf("%lf%lf%lf%lf",&f[j].p1.x,&f[j].p1.y,&f[j].p2.x,&f[j].p2.y);
                for(k=1;k<j;k++)
                {
                    if(judge(f[k].p1,f[k].p2,f[j].p1,f[j].p2))
                        unionset(k,j);
                }
                j++;
            }
        }
        if(T)
            puts("");
    }
    return 0;
}







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值