区间重合判断(pojg校门外的树)

pojg;http://poj.grids.cn/practice/2808

解法1:以空间换时间:

#include<stdio.h>
#include<string.h>
int main()
{
    int L,M;
    scanf("%d%d",&L,&M);
    bool *a=new bool[10001];
    memset(a,1,sizeof(bool)*10001);

    int m,n;
    for(int i=1;i<=M;i++)
    {
        scanf("%d%d",&m,&n);
        for(int j=m;j<=n;j++)
        {
            a[j]=false;
        }
    }
    int sum=0;
    for(int i=0;i<=L;i++)
    {
        if(a[i]==true)
            sum++;
    }
    printf("%d
",sum);
}

2:合并区间):

想是将所有区间存储在数组里面,对所有区间以下限为标准排序,然后从头至尾扫描区间数组,
合并区间的方法是:当前区间初始化为第一个区间,然后判断下一个区间的下限是否已经超过当前区间的上限,如果是这样的话,就无法继续合并了,那么就继续已经合并区间的长度,重新开始一次新的合并,否则的话,将下一个区间合并到当前区间起来。。。

#include<stdio.h>
#include<stdlib.h>
typedef struct Line{ int start; int end; }Line; int compareLine(const void *a,const void *b) { return ((Line *)a)->start-((Line *)b)->start; } int main() { Line line[1001],tmp; int L,M; scanf("%d%d",&L,&M); for(int i=0;i<M;i++) { scanf("%d%d",&line[i].start,&line[i].end); } qsort(line,M,sizeof(Line),compareLine); int count=0; tmp=line[0]; for(int i=1;i<M;i++) { if(line[i].start<=tmp.end) { tmp.end=line[i].end; } else { count=count+tmp.end-tmp.start+1; tmp=line[i]; } } count=count+tmp.end-tmp.start+1;//为什么 printf("%d ",L+1-count); }

我们以(1,2) (2,4) (4,5) (6,7)  (9,12)

开始tmp=line[0].

i=1 合并 tmp.end=4;

i=2 合并 tmp.end=5;

i=3; count=0+ (5-1)+1=5; tmp=line[3];

i=4; tmp.end<line[4].start; count=5+(7-6)+1=7; tmp=line[4];

退出

count=7+(12-9)+1=11;

参考:http://www.cppblog.com/csu-yx/archive/2011/11/07/159746.html

如果L很大,比如是40亿,无法开辟这么大的数组,怎么办?采用第二种方法可以。

编程之美也有类似的这个题目,其实,种树问题本质是区间重合判断。

一,问题:

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

       2. 给定一个窗口区域和系统界面上的N个窗口,判断这个窗口区域是否被已有的窗口覆盖。

第一题源代码:

按照编程之美给出的提示。 先用区间的左边界值对目标区间进行排序O(nlogn),对排好序的区间进行合并O(n),对每次待查找的源区间,用二分查出其左右两边界点分别处于合并后的哪个源区间中O(logn),若属于同一个源区间则说明其在目标区间中,否则就说明不在。(为什么可以二分法)

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

#define MAX 10001
struct Line
{
    int low,high;
    bool operator<(const Line &L) const
    {
        return low<L.low;
    }
};
 

Line lines[MAX];   // 目标区间  
int ncount=0;//合并后区间个数

//用二分查找找出key所在的区间,以区间的low作为划分  
int getIndex(int key)
{
    int u,v;
    u=0;
    v=ncount-1;
    while(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;//目标区间个数
    cout<<"输入目标区间个数"<<endl;
    cin>>n;
    cout<<"输入目标区间"<<endl;
    for(int i=0;i<n;i++)
    {
        cin>>lines[i].low>>lines[i].high;
        cout<<endl;
    }
    cout<<"输入源区间个数"<<endl;
    int k;
    cin>>k;
    Line source[1001];
    for(int i=0;i<k;i++)
    {
        cin>>source[i].low>>source[i].high;
        cout<<endl;
    }
    //排序O(nlgn)
    sort(lines,lines+n);
    int tmp=lines[0].high;
    for(int i=1;i<n;i++)
    {
        if(tmp>=lines[i].low)
        {
            tmp=lines[i].high;
        }
        else
        {
            lines[ncount++].high=tmp;
            lines[ncount].low=lines[i].low;
            tmp=lines[i].high;
        }
    }
    lines[ncount++].high=tmp;
    
    for(int i=0;i<k;i++)
    {
        int s1=getIndex(source[i].low);
        int s2=getIndex( source[i].high);
        if(s1==s2 && source[i].high<=lines[s2].high)
        {
            cout<<"在区间内"<<endl;
        }
        else
        {
            cout<<"不再区间内"<<endl;
        }
    }
 

}

 上面的程序有点错误:

在if (lasthigh >= lines[i].low) 
lasthigh = lines[i].high; 
不能直接修改lasthigh值,要判断if(lasthigh<lines[i].high)才能修改 如{1,5}{2,4}则会合并成{1,4}.

转自:http://blog.csdn.net/tianshuai1111/article/details/7828961

下面的程序好点:

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

struct Line{
    int low,high;
    Line(int l=0,int h=0):low(l),high(h){}
    bool operator<(Line & other)
    {
        return low<other.low;
    }
};

const int MAX = 100;
Line lines[MAX];

//注意咱们搜索的数为刚好小于key的那个值
int BinarySearchLower(Line arr[],int len,int target)
{
    int low = 0;
    int high = len-1;

    while(low < high){
        int mid = (low + high)/2;
        if(arr[mid].low>=target)high = mid-1;
        else low = mid + 1;
            
    }
    return high;
}


int main()
{
    int n, k, i, j;  
    
    cin>>n;  // n是目标区间的个数,k是待查询的源区间的个数  
    for (i=0; i<n; i++)  
        cin >> lines[i].low >> lines[i].high;  

    sort(lines, lines+n);  
 
    int cnt = 0; //合并以后的区间数
    int lasthigh = lines[0].high;  

        //合并区间
    for(i=1; i<n; i++){
        if(lasthigh>=lines[i].low && lasthigh<lines[i].high)
            lasthigh = lines[i].high;
        else{
            lines[cnt].high = lasthigh;
            lines[++cnt].low = lines[i].low;
            lasthigh = lines[i].high;
        }

    }
    lines[cnt++].high = lasthigh;

    Line search = Line(1,6);

    int sLow = BinarySearchLower(lines,cnt,search.low);
    int sHigh = BinarySearchLower(lines,cnt,search.high);
    if(sLow==sHigh && search.high<=lines[sLow].high)//注意要判断
        cout<<"Yes"<<endl;
    else cout<<"No"<<endl;

    system("pause");
    return 0;
}

我们输入三段目的区间:

1 4

2 3

7 8

要查找的是1-5,

经过合并后lines变成了:

1 4

2 3 

78 

注意2,3并没有去掉,在查找的时候有用。

 上面的程序有问题。

解法:使用并查集

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

#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");
}

更多:http://blog.csdn.net/tianshuai1111/article/details/7828961

 

画图理解:

原文地址:https://www.cnblogs.com/youxin/p/3266617.html