1. 程式人生 > >備戰藍橋杯決賽----堅持第六天!!!

備戰藍橋杯決賽----堅持第六天!!!

     今天的學習時間是從晚6點開始,一道簡單的題目,消耗了我三個半小時,其實是先看了一道關於hash的題目,沒有看懂,就用看了一道題目,正好碰到了簡單的並查集。雖然今天也是很苦逼,但是心情似乎沒有那麼差了,也許是我來學習之前就想明白了,不想說自己生活上的事情,我們單單對於演算法來講,千萬不要因為你沒有AC一道題目,沒有看懂一道題目的解析而感到沮喪,傷心。。更不能把這種情緒帶到生活的其他方面。反而我們可以通過其他事情,來緩和我們對於沒有AC的沮喪心情。

題目:

問題描述  w星球的一個種植園,被分成 m * n 個小格子(東西方向m行,南北方向n列)。每個格子裡種了一株合根植物。
  這種植物有個特點,它的根可能會沿著南北或東西方向伸展,從而與另一個格子的植物合成為一體。


  如果我們告訴你哪些小格子間出現了連根現象,你能說出這個園中一共有多少株合根植物嗎?輸入格式  第一行,兩個整數m,n,用空格分開,表示格子的行數、列數(1<m,n<1000)。
  接下來一行,一個整數k,表示下面還有k行資料(0<k<100000)
  接下來k行,第行兩個整數a,b,表示編號為a的小格子和編號為b的小格子合根了。


  格子的編號一行一行,從上到下,從左到右編號。
  比如:5 * 4 的小格子,編號:
  1 2 3 4
  5 6 7 8
  9 10 11 12
  13 14 15 16
  17 18 19 20樣例輸入5 4
16
2 3
1 5
5 9
4 8
7 8
9 10
10 11
11 12
10 14
12 16
14 18
17 18
15 19
19 20
9 13
13 17樣例輸出5樣例說明  其合根情況參考下圖

剛開始我也是沒有思路,然後百度,看到其他博主對此題分析的第一句話,都是簡單,並查集操作。然後我就立刻停止了瀏覽,自己寫了起來,因為自己之前看過並查集,感覺再回憶一下,應該能AC這題。所以我又理了一遍並查集,如果有不懂並查集的,可以先跳過下面的答案,看看我下面並查集的構建過程。

程式碼:

#include<iostream>
using namespace std;
const int MAX=1000001;//陣列範圍一定要明確 
int parent[MAX];
int rank[MAX];
int find(int p){
     while(parent[p]!=p){
      	parent[p] = parent[parent[p]];
        p=parent[p];
		}
		return p;
}
void uniono(int p,int q){
     int pID = find(p);
	 int qID = find(q);
	 if(pID==qID){
	 	return;
	 }
	 if(rank[pID]>rank[qID]){
	 parent[qID]=pID;	
	 }else if(rank[pID]<rank[qID]){
	 	parent[pID]=qID;
	 }else{
	 	parent[qID]=pID;
	 	rank[pID]+=1;
	 }
	 	
}
int main(){
	int m,n;
	cin>>m>>n;
	int k=m*n;
	for(int i=1;i<=k;i++){
		parent[i]=i;
		rank[i]=1;
	}
    int x,y,l;
    cin>>l;
    for(int i=0;i<l;i++){
    	cin>>x>>y;
    	uniono(x,y);
	}
	int *p = new int[k+1];
	for(int i=1;i<=k;i++){
		p[find(i)]=1;//這裡之前寫成parent[i]就一直不能通過,改為find(i)後可以通過,至於原因,想到再說吧 
	}
	int s=0;
	for(int i=1;i<=k;i++){
		if(p[i]==1){
			s+=1;
		}
	}
	cout<<s;
	return 0;
}

並查集的構建,關鍵在於需要設定兩個方法:uniono(合併)與find(查詢)

原始的簡單方法:

int id[10];//構建一個id陣列,用於表示節點間的關係。如:陣列奇數元素定義為0,偶數元素定義為1,則表示奇數元素節點互相相連,偶數元素節點互相相連
int find(int p){
	return id[p];
}
void uniono(int p,int q){
     int pID = find(p);
	 int qID = find(q);
	 if(pID==qID){
	 	return;
	 }else{
	 	id[pID]=id[qID];
	 }	
}

採用父節點的思想,所有連線節點屬於一個父節點

int parent[10];
int find(int p){
     while(parent[p]!=p){
       p=parent[p];
		}
 return p;
}
void uniono(int p,int q){
     int pID = find(p);
	 int qID = find(q);
	 if(pID==qID){
	 	return;
	 }
	 parent[pID]=qID;	
}

基於rank的優化

int parent[10];
int rank[10];
int find(int p){
     while(parent[p]!=p){
       p=parent[p];
		}
	return p;
}
void uniono(int p,int q){
     int pID = find(p);
 int qID = find(q);
 if(pID==qID){
 return;
 }
 if(rank[pID]>rank[qID]){
 parent[qID]=pID;
 }else if(rank[pID]<rank[qID]){
 parent[pID]=qID;
 }else{
 parent[qID]=pID;
 rank[pID]+=1;
 }
 
}

路徑壓縮:

int parent[10];
int rank[10];
int find(int p){
     while(parent[p]!=p){
       	p = parent[parent[p]];
       p=parent[p];
		}
		return p;
}
void uniono(int p,int q){
     int pID = find(p);
	 int qID = find(q);
	 if(pID==qID){
	 	return;
	 }
	 if(rank[pID]>rank[qID]){
	 parent[qID]=pID;	
	 }else if(rank[pID]<rank[qID]){
	 	parent[pID]=qID;
	 }else{
	 	parent[qID]=pID;
	 	rank[pID]+=1;
	 }
	 	
}