區間重合判斷(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\n",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\n",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

 

 

 

 

 

 

畫圖理解:

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM