【编程之美】2.19 区间重合判断

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

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

#include <stdio.h>
#include <stdlib.h>

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;
}

看网上别人的答案。http://blog.csdn.net/tianshuai1111/article/details/7828961中的思路

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

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

#include <iostream>
#include <algorithm>
using namespace std;

struct Line
{
    int low, high;
    bool operator<(const Line &l) const
    {return low<l.low;}
};

#define MAXN 10001
Line lines[MAXN];    // 目标区间
int ncnt = 0;        // 合并后区间的个数

#define N 101
Line sl[N];            // 待查询的源区间

// 用二分查找找出key所在的区间,以区间的low作为划分
int GetIndex(int key)
{
    int u, v;
    u = 0; v = ncnt-1;
    while (u<=v)    // u,v可取等号
    {
        int m = (u+v)>>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<n; i++)
        cin >> lines[i].low >> lines[i].high;
    for (i=0; i<k; i++)
        cin >> sl[i].low >> sl[i].high;
    // 排序O(nlogn)
    sort(lines, lines+n);
    // 合并O(n)
    int lasthigh = lines[0].high;
    for (i=1; i<n; i++)
        if (lasthigh >= 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<k; i++)
    {
        // 单词查找时间O(logn)
        int s1 = GetIndex(sl[i].low);
        int s2 = GetIndex(sl[i].high);
        if (s1==s2 && sl[i].high <= lines[s2].high)
            printf("Yes\n");
        else
            printf("No\n");
    }
}

法二:使用并查集,对每个区间合并到一个子树上,最后判断源区间的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<iostream>
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");
}

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

时间: 2024-10-11 16:57:31

【编程之美】2.19 区间重合判断的相关文章

2.19 区间重合判断

问题: 1. 给定一个源区间[x,y]和N个无序的目标区间[x1,y1] [x2,y2] ... [xn,yn],判断源区间[x,y]是不是在目标区间内. 2. 给定一个窗口区域和系统界面上的N个窗口,判断这个窗口区域是否被已有的窗口覆盖. 1. 解法: 先用区间的左边界值对目标区间进行排序O(nlogn),对排好序的区间进行合并O(n),对每次待查找的源区间,用二分查出其左右两边界点分别处于合并后的哪个源区间中O(logn),若属于同一个源区间则说明其在目标区间中,否则就说明不在. 代码: #

区间重合判断[poj2808 校门外的树]

题目:http://bailian.openjudge.cn/practice/2808/ 参考了文章,重写了代码:http://www.cnblogs.com/youxin/p/3266617.html(注:原文解法2代码有误) 解法1:以空间换时间 #include <iostream> using namespace std; int main() { int L, M, i, start, end, count; bool trees[10001]; for (i = 0; i <

第2章 数字之魅——区间重合判断

区间重合判断 问题描述 分析与解法 [解法一] 具体代码如下: 1 package chapter2shuzizhimei.qujianchonghe; 2 /** 3 * 区间重合判断 4 * @author DELL 5 * 6 */ 7 public class IntervalOverlap { 8 //区间类 9 public static class Interval{ 10 public double x; //区间左端点 11 public double y; //区间右端点 1

编程之美——区间重合判断

将源区间按照x坐标大小排序,然后进行合并,使用二分查找目标区间x,y坐标在源区间中的分布,若两者分布于同一连续区间,则区间重合 代码: 1 #include<iostream> 2 #include<algorithm> 3 using namespace std; 4 5 struct section 6 { 7 int low; 8 int high; 9 bool operator<(const section &l)const 10 { 11 return l

【编程之美】区间重合判断

给定一个源区间[x,y](y>=x)和N个无序的目标区间[x1,y1] [x2,y2] [x3,y3] [xn,yn],判断源区间[x,y]是不是在目标区间内? 例如:给定源区间[1,6]和一组无序的目标区间[2,3] [1,2] [3,9],即可认为区间[1,6]在区间[2,3] [1,2] [3,9]内(因为目标区间实际上是[1,9]). 分析与解法 方法一:直接法 一个比较直接的思路即将源区间和目标区间逐个投影到坐标轴上,只考察源区间未被覆盖的部分.如果所有的区间全部投影完毕,仍然有源区间

[算法]区间重合判断

题目描述: 给定一个源区间 [x,y]和N个无序的目标区间[x1,y1],[x2,y2],...[xn,y,],判断给定的源区间[x,y]在不在目标区间内. 例如:给定源区间[1 6]和目标区间[1 2][2 4][4 9]即可认为区间[1 6]在目标区间内,因为源区间的并集为[1 9 ]. 试想一下,现在在这样的一个目标区间的集合, 需要频繁地去查询一个区间是否在该集合中.那么怎么样才能降低单次查询的复 杂度呢.预处理.对区间的预处理可以满足这样的需求. 直接上方法: 第一步: 首先对区间进行

【编程之美】目录

第1章  游戏之乐——游戏中碰到的题目 1.1 让CPU占用率听你的指挥 1.2 中国象棋将帅问题 1.3 一摞烙饼的排序 1.4 买书问题 第2章  数字之魅——数字中的技巧 2.1 求二进制中1的个数 2.2 不要被阶乘吓倒 2.3 寻找发帖"水王" 2.4 1的数目 2.5 寻找最大的K个数 2.6 精确表达浮点数 2.7 最大公约数问题 2.8 找符合条件的整数 2.9 斐波那契(Fibonacci)数列 2.10 寻找数组中的最大值和最小值 2.11 寻找最近点对 2.12

编程之美需整理的题目汇总

2.1 求二进制中1的个数. 2.2 求n!末尾有多少个0 和 求n!的二进制表示中最低位1的位置. 2.3 找出数量超过总数一半的记录. 扩展:3个发帖都超过了总数N的1/4. 2.4 十进制1~N 所有整数中1的个数: 满足F(N)=N的最大的N是多少. 扩展:CC上所有2的个数. 2.5 寻找最大的k的个数. 2.6 精确表达浮点数 2.7 最大公约数问题. 2.8 任意给定一个正整数N,求一个最小的正整数M(M>1),似的N*M的十进制表示形式里只有1和0. 2.9 Fibonacci

《编程之美》3.6判断链表是否相交之扩展:链表找环方法证明

先看看原题:<编程之美>3.6编程判断两个链表是否相交,原题假设两个链表不带环. 为了防止剧透使得没看过原题目的读者丧失思考的乐趣,我把最好的解法隐藏起来.由于这个问题本身的解答并不是本文的重点,扩展问题也采用这种形式呈现. 注:位于(*)符号之间的文字出自于:http://blog.csdn.net/v_july_v/article/details/6447013,作者v_JULY_v. 用指针p1.p2分别指向两个链表头,不断后移:最后到达各自表尾时,若p1==p2,那么两个链表必相交 用