码迷,mamicode.com
首页 > 其他好文 > 详细

(hdu step 5.1.3)Segment set(求与一条线段相交的线段集合中的线段的数量)

时间:2015-02-28 20:24:05      阅读:189      评论:0      收藏:0      [点我收藏+]

标签:



题目:

Segment set

Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 177 Accepted Submission(s): 82
 
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
 
Author
LL
 
Source
HDU 2006-12 Programming Contest
 
Recommend
LL
 


题目大意:

               在此对“线段集合”的概念解释一下:有A,B,C三条线段。如果A与B相交,B与C相交(线段A与线段C可以不直接相交).那么这时候A,B,C依然属于同一个线段集合。在输入样例时,遇到P代表的是添加一条新边,后面跟的是一条边的

起点的横纵坐标、终点的横纵坐标


题目分析:

               计算几何+并查集。其实在这里只是用到了计算集合中的"相交"的概念,判断两条线段是否相交。大体思路就是,添加线段的时候,如果两条线段相交,就将着两条线段合并到同一个线段集合中。然后在Q的时候查询某一条

线段所在的线段集合中的线段的数量。


代码如下:

/*
 * c.cpp
 *
 *  Created on: 2015年2月28日
 *      Author: Administrator
 */

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>


using namespace std;

const int maxn = 1001;
int father[maxn];//用于保存父子关系
int r[maxn];//用于保存一某一个结点为根节点的权重。(在这里可以理解成某一个线段集合中的点段的数量).例如r[a]表示的是以a为根节点的集合的权重
int counter;//当前的边的数量


/**
 * 寻找某一个结点所在集合的根节点
 */
int find(int a) {
	if (a == father[a]) {
		return a;
	}

	return father[a] = find(father[a]);
}

/**
 * 合并a结点所在的集合与结点b所在的集合
 */
void join(int a, int b) {
	int fa = find(a);//找到结点a所在集合的根节点
	int fb = find(b);//找到结点b所在集合的根节点

	if (fa != fb) {//如果a所在集合的根节点与b所在集合的根节点不相同
		father[fa] = fb;//则合并这两个集合.在这里执行的操作室将a所在集合的根节点fa指向结点b所在集合的根节点fb
		r[fb] += r[fa];//将结点fa的权重加到结点fb上

		/**
		 * 将结点fa的权重置0.为什么要这样做呢?
		 * 主要是为了避免权重的重复相加。这是由后面的addEdge()这个函数的执行操作所决定的。
		 * 每次有新的边时,addEdge()都会遍历边集合中的所有边,判断其是否与新的边相交,
		 * 如果相交,则执行并操作join().这时候其中一条鞭的权值就会移到另一条边上.
		 * 例如有序号分别为1,2,3的三条边。他们依次添加道边的集合上.它们的权重分别是1,1,1.如果这三条边都属于同一个集合,
		 * 那么这个集合的权重应该是1+1+1=3.
		 *
		 * 在添加边2的时候,假如边1和边2相交,那么这时候边2的权重是2
		 * 如果不把结点1的权重置0,那么在边3月边2、边1都相交的情况下,
		 * 那么经过合并操作以后,得出的并查集的权重是1+2+1=4了,这很明显是不正确的
		 */
		r[fa] = 0;
	}
}

struct Point {//点
	double x;//横坐标
	double y;//纵坐标
};

struct Edge {//边
	Point start;//起点
	Point end;//终点
}edges[maxn];//边集合


double multiply(Point p1, Point p2, Point p0) {
	return ((p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y));
}
//a(x1,y1),b(x2,y2)
//x1*y2-x2*y1

/**
 * 确定两条线段是否相交
 */
int intersect(Edge u, Edge v) {
	return ((max(u.start.x, u.end.x) >= min(v.start.x, v.end.x)) &&  //u中最右的点是否在v最左的点的右边
			(max(v.start.x, v.end.x) >= min(u.start.x, u.end.x)) && //v中最右的点是否在u最左的点的右边
//判断这两条线段在水平层面上是否可能相交
			(max(u.start.y, u.end.y) >= min(v.start.y, v.end.y)) && //u中最上的点是否在v最下的点的上边
			(max(v.start.y, v.end.y) >= min(u.start.y, u.end.y)) && //v中最上的点是否在u最下的点的上边
//判断这两条线段在垂直层面上是否可能相交
			(multiply(v.start, u.end, u.start) * multiply(u.end, v.end, u.start) >= 0) &&
//判断v.start,v.end是否分布在u.end两侧(或线上)
			(multiply(u.start, v.end, v.start) * multiply(v.end, u.end, v.start) >= 0));
	//判断u.start,u.end是否分布在v.start两侧(或线上)
}


/**
 * 添加边的操作
 */
void addEdge(){
	int i;
	for(i = 1 ; i < counter ; ++i){//新添加的边与目前边集合中的所有边作比较
		if(intersect(edges[i],edges[counter]) == true){//判断它们是否相交
			join(i,counter);//如果相交则执行合并操作
		}
	}
}

/**
 * 初始化
 */
void init(){
	int i;
	for(i = 1 ; i < maxn ; ++i){//遍历所有节点.索引从1开始,不要从0开始.否则会有一些问题.认真想想为什么
		father[i] = i;//所有节点的父亲节点一开始默认都是它自己
		r[i] = 1;//所有节点默认的权重都是1
	}
}

int main() {
	int t;
	scanf("%d",&t);

	int cas = 0;//主要用于没测测试用例后面都打印一个空行

	while(t--){
		if(cas != 0){//需要注意的是同样的表述.有的题目中全部打空行可以AC,有的必须最后一个不能打空行
			printf("\n");
		}
		cas++;

		counter = 1;//边数从1开始
		init();

		int m;
		scanf("%d",&m);

		string str;

		int i;
		for(i = 1 ; i <= m ; ++i){//需要注意一下这种形式的输入样例
			cin >> str;
			if(str[0] == ‘P‘){
				scanf("%lf%lf%lf%lf",&edges[counter].start.x,&edges[counter].start.y,&edges[counter].end.x,&edges[counter].end.y);
				addEdge();

				counter++;
			}else{
				int index;
				scanf("%d",&index);
				printf("%d\n",r[find(index)]);
			}
		}

//		printf("\n");
	}

	return 0;
}











(hdu step 5.1.3)Segment set(求与一条线段相交的线段集合中的线段的数量)

标签:

原文地址:http://blog.csdn.net/hjd_love_zzt/article/details/43988553

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!