Segment set 并查集、几何计算

Segment set   HDU - 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.

 

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

题意:t组输入,每组一个数m,表示m次操作。接下来m行,每行先输入一个字符ok,若ok==‘P’,则表示添加线段,然后紧接着输入线段的两个坐标;如果ok==‘Q’,再输入一个数k,表示询问与k所在的集合中有多少条线段。每个测试用例之间有一个空白行。

分析:首先,我们肯定要判断两条线段是否相交,如何判断呢?代码中给出的方法并不是规范的判断方法。其次,就是要进行集合操作,很自然的就想到了并查集。我们可以开两个数组,pre数组就是普通的根结点数组,而num数组是为了统计根结点所在结合的线段数。在每次添加一条线段时,我们要判断该线段是否跟以前的线段有交点,如果有交点,则进行unionn操作,即合并。在unionn函数中,如果两个参数的根结点不同,则合并,并且num数组也得发生变化。

注:最后一个测试用例之后没有空白行。

代码:

#include<iostream>
#include<cstdio>
#include<string.h>
using namespace std;
struct point{
	double x,y;
};
struct line{
	point a,b;
}l[1005];
int pre[1005],num[1005];
const double EPS=1e-10;
void init()
{
	for(int i=0;i<1005;i++)
	{
		pre[i]=i;
		num[i]=1;//一开始每个集合只有一个线段,就是自己  
	}
}
bool judge(line l1,line l2)//判断两个线段是否相交   
{
	point p1,p2,p3,p4;
    p1=l1.a;p2=l1.b;
    p3=l2.a;p4=l2.b;
 
    if( min(p1.x,p2.x)>max(p3.x,p4.x) ||
       min(p1.y,p2.y)>max(p3.y,p4.y) ||
       min(p3.x,p4.x)>max(p1.x,p2.x) ||
       min(p3.y,p4.y)>max(p1.y,p2.y) )
        return 0;
    double k1,k2,k3,k4;
    k1 = (p2.x-p1.x)*(p3.y-p1.y) - (p2.y-p1.y)*(p3.x-p1.x);
    k2 = (p2.x-p1.x)*(p4.y-p1.y) - (p2.y-p1.y)*(p4.x-p1.x);
    k3 = (p4.x-p3.x)*(p1.y-p3.y) - (p4.y-p3.y)*(p1.x-p3.x);
    k4 = (p4.x-p3.x)*(p2.y-p3.y) - (p4.y-p3.y)*(p2.x-p3.x);
    return (k1*k2<=EPS && k3*k4<=EPS);
}
int find(int x)
{
	while(x!=pre[x])
		x=pre[x];
	return x;
}
void unionn(int x,int y)
{
	int r1=find(x);
	int r2=find(y);
	if(r1!=r2)
	{
		pre[r2]=r1;
		num[r1]+=num[r2];
	}
}
int main()
{
	int t,m,cnt,k;
	char ok;
	cin>>t;
	while(t--)
	{
		cin>>m;
		cnt=0;
		init();
		for(int i=0;i<m;i++)
		{
			cin>>ok;
			if(ok=='P')
			{
				cnt++;
				cin>>l[cnt].a.x>>l[cnt].a.y>>l[cnt].b.x>>l[cnt].b.y;
				for(int j=1;j<cnt;j++)//j从1开始,不能从0开始  
				{
					if(judge(l[cnt],l[j]))
						unionn(cnt,j);
				}
			}
			else if(ok=='Q')
			{
				cin>>k;
				int root=find(k);
				cout<<num[root]<<endl;
			}
		}
		if(t)//注意格式  
			cout<<endl;
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值