博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【编程之美】2.19 区间重合判断
阅读量:4568 次
发布时间:2019-06-08

本文共 5322 字,大约阅读时间需要 17 分钟。

题目: 给定一个目标区间[x,y]和N个无序的源区间[x1,y1] [x2,y2] ... [xn,yn],判断源区间[x,y]是不是在目标区间内。

 

思路:把源区间按x从小到大排序,再把区间依次合并,每完整得到一个合并区间后,判断[x,y]是否在合并区间内。

#include 
#include
typedef struct { int x; int y;}Region;int cmp(const void * a, const void * b) //由小到大排序{ return *(int *)a - *(int *)b;}bool isInRegion(Region dst, Region * src, int N){ qsort(src, N, sizeof(src[0]), cmp); //按输入的x 从小到大排序 Region currentRegion = src[0]; for(int i = 1; i < N; i++) { if(src[i].y <= currentRegion.y) //判断的区间已经包含在当前区间 { continue; } else { if(src[i].x > currentRegion.y) //出现了新的区间 与当前区间不交叠 { if(dst.x >= currentRegion.x && dst.y <= currentRegion.y) //若目标区间在已经截断的区间内 返回true { return true; } else //否则 把当前区间改成新出现的区间 { currentRegion = src[i]; } } else { currentRegion.y = src[i].y; } } } if(dst.x >= currentRegion.x && dst.y <= currentRegion.y) //若目标区间在已经截断的区间内 返回true { return true; } else { return false; }}int main(){ Region src[3] = {
{
2,3},{
1,2},{
4,9}}; Region dst = {
1,6}; bool b = isInRegion(dst, src, 3); return 0;}

 

看网上别人的答案。中的思路

先用区间的左边界值对目标区间进行排序O(nlogn),对排好序的区间进行合并O(n),对每次待查找的源区间,用二分查出其左右两边界点分别处于合并后的哪个源区间中O(logn),若属于同一个源区间则说明其在目标区间中,否则就说明不在。

感觉这个解法中虽然说查找处于合并后的哪个区间只要O(logn)但是必须要先把所有的区间合并完。我的方法虽然要对每一个合并区间判断,但是如果中间就找到了后面就不用做了。

#include 
#include
using namespace std;struct Line{ int low, high; bool operator<(const Line &l) const {
return low
>1; if (key >= lines[m].low) u = m+1; else v = m-1; } return v;}int main(){ int n, k, i, j; cin >> n >> k; // n是目标区间的个数,k是待查询的源区间的个数 for (i=0; i
> lines[i].low >> lines[i].high; for (i=0; i
> sl[i].low >> sl[i].high; // 排序O(nlogn) sort(lines, lines+n); // 合并O(n) int lasthigh = lines[0].high; for (i=1; i
= lines[i].low) lasthigh = lines[i].high; else { lines[ncnt++].high = lasthigh; lines[ncnt].low = lines[i].low; lasthigh = lines[i].high; } lines[ncnt++].high = lasthigh; for (i=0; i

法二:使用并查集,对每个区间合并到一个子树上,最后判断源区间的x和y的根是否相同。

----------------------------------------------------------------------------------------------

补充并查集知识:

描述

若某个 人员过于庞大,要判断两个是否是亲戚,确实还很不容易,给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关系。 规定:x和y是亲戚,y和z是亲戚,那么x和z也是亲戚。如果x,y是亲戚,那么x的亲戚都是y的亲戚,y的亲戚也都是x的 。

Input

第一行:三个整数n,m,p,(n< =5000,m< =5000,p< =5000),分别表示有n个人,m个亲戚关系,询问p对亲戚关系。 以下m行:每行两个数Mi,Mj,1< =Mi,Mj< =N,表示Mi和Mj具有亲戚关系。 接下来p行:每行两个数Pi,Pj,询问Pi和Pj是否具有亲戚关系。

Output

P行,每行一个’Yes’或’No’。表示第i个询问的答案为“具有”或“不具有”亲戚关系。

分析——问题实质

  初步分析觉得本题是一个图论中判断两个点是否在同一个连通子图中的问题。对于题目中的样例,以人为点,关系为边,建立无向图如下:
比如判断3和4是否为亲戚时,我们检查3和4是否在同一个连通子图中,结果是在,于是他们是亲戚。又如7和10不在同一个连通子图中,所以他们不是亲戚。
用图的数据结构的最大问题是,我们无法存下多至(M=)2 000 000条边的图,后面关于算法时效等诸多问题就免谈了。
用图表示关系过于“奢侈”了。其实本题只是一个对分离集合(并查集)操作的问题。
我们可以给每个人建立一个集合,集合的元素值有他自己,表示最开始时他不知道任何人是它的亲戚。以后每次给出一个亲戚关系a, b,则a和他的亲戚与b和他的亲戚就互为亲戚了,将a所在集合与b所在集合合并。对于样例数据的操作全过程如下:
输入关系 分离集合
初始状态
(2,4) {2,4}
(5,7) {2,4} {5,7}
(1,3) {1,3} {2,4} {5,7}
(8,9) {1,3} {2,4} {5,7} {8,9}
(1,2) {1,2,3,4} {5,7} {8,9}
(5,6) {1,2,3,4} {5,6,7} {8,9}
(2,3) {1,2,3,4} {5,6,7} {8,9}
最后我们得到3个集合{1,2,3,4}, {5,6,7}, {8,9},于是判断两个人是否亲戚的问题就变成判断两个数是否在同一个集合中的问题。如此一来,需要的数据结构就没有图结构那样庞大了。
算法需要以下几个子过程:
(1) 开始时,为每个人建立一个集合SUB-Make-Set(x);
(2) 得到一个关系后a,b,合并相应集合SUB-Union(a,b);
(3) 此外我们还需要判断两个人是否在同一个集合中,这就涉及到如何标识集合的问题。我们可以在每个集合中选一个代表标识集合,因此我们需要一个子过程给出每个集合的代表元SUB-Find-Set(a)。于是判断两个人是否在同一个集合中,即两个人是否为亲戚,等价于判断SUB-Find-Set(a)=SUB-Find-Set(b)。
--------------------------------------------------------------------------------------------------------------
大神,方法二中 rank表示这个元素是几个层的代表元素, father表示区间的代表元素,如下,rank[1] = 3  rank [2] = 1  rank[6] = 2 
              1-9
        /       |     \
    1-4     2-7   6-9
  /     \
1-3   2-4
但是这个方法的一个重要问题是,如果只有两个区间 【1-10000000】【600000000-400000000000】这样数字很大的,那还要对数字中所有的元素循环,
不划算。
#include
using namespace std;const int size = 100;int father[size];int rank[size];void make_set(int n){ for(int i = 1; i <= n; i ++){ father[i] = i; rank[i] = 1; } }int find_set(int x)//寻找代表元素 { if(x != father[x]){ //元素不是单独的段,在某个区间内,返回某个区间代表 father[x] = find_set(father[x]); } return father[x];}void Union(int x, int y){ x = find_set(x); y = find_set(y); if(x == y){ //两个在同一个区间 return ; } if(rank[x] < rank[y]){ father[x] = y; } else{ father[y] = x; if(rank[x] == rank[y]){ rank[x] ++; } }}int main(){ int x1, y1; cin >> x1 >> y1;//输入要判断区间 int x, y; int n; cin >> n; //区间的个数 make_set(size); while(n --){ cin >> x >> y; //输入每个区间 if(x > y){
//这一步很关键,表示考虑的周到 swap(x, y); } for(int i = x + 1; i <= y; i++){
//将区间内的 段合并到已有区间 Union(x, i); } } if(find_set(x1) == find_set(y1)){ cout << "yes" << endl; } else{ cout << "no" << endl; } system("pause");}

 

 

这道题后面还有个二维区间重合判断的,说是要用到线段树。还没看完,之后单独写吧。

转载于:https://www.cnblogs.com/dplearning/p/4094930.html

你可能感兴趣的文章
OUTLOOK+VBA 备份邮件到GMAIL
查看>>
谁说delphi没有IOCP库,delphi新的IOCP类库,开源中: DIOCP组件JSON流模块说明
查看>>
[四种方法]检测数据类型
查看>>
chrome使用技巧 值得珍藏
查看>>
【IT笔试面试题整理】数组中出现次数超过一半的数字
查看>>
cocos下的UI编辑器--BoomEditor使用教程(1)--快速上手
查看>>
关于树的字段设计
查看>>
display, position和float的关系
查看>>
查看linux错误日志
查看>>
go语言之进阶篇 select实现的超时机制
查看>>
C++ Primer 有感(异常处理)(三)
查看>>
支付宝支付
查看>>
lambda表达式多条件查询
查看>>
[百度之星2014资格赛] Disk Schedule 报告
查看>>
控制台应用程序《石头剪刀布》——新手,
查看>>
移动前端自适应解决方案和比较
查看>>
NSString用法总结
查看>>
Use formatter to format your JAVA code
查看>>
Go prepare statment超过mysql最大数
查看>>
MySQL命令:约束
查看>>