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
畫圖理解: