歷屆試題 合根植物


問題描述

  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
樣例說明
  其合根情況參考下圖
 
前言:做這道題的時候,第一感覺很簡單,但是讀完題的時候很蒙,一開始沒弄明白合根植物的意思,之后就是沒有好的方法去處理計算合根植物的個數,百度之后才知道要用並查集這個數據結構,之前沒有了解過,就花了時間去學習,搞懂之后才發現原來就是並查集啊,使用並查集根本就不需要考慮那么多,直接計算有多少個陣營(這道題里面應該是所有合根植物聚合之后的大的合根植物的個數),當然之前有接觸這個並查集的大佬們,這道題很簡單,但是沒有學過這個的,應該很難處理這道題。具體不清楚並查集的可以看下篇博客 https://www.cnblogs.com/henuliulei/p/10546264.html
 
思路:看上面的參考圖,要求的是合根的個數,其實就是連通子集的個數,注意6也是一個連通子集,使用並查集可以快速求出連通子集,但要注意算法的壓縮。注意因為m,n取值較大,所以考慮
一維數組的開辟空間使用0x3f3f3f
 
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int MAX = 0x3f3f3f;
 4 int pre[MAX];
 5 int m,n;
 6 int s;
 7 int left1,right1;
 8 void init(int n){
 9   for(int i=1;i<=n;i++){
10     pre[i]=i;
11   }
12 }
13 int find_pre(int key)
14 {
15     int temp=key;
16     while(temp!=pre[temp]){
17         temp=pre[temp];
18     }
19     int t=0;
20     while(key!=temp){//壓縮
21         t=pre[key];
22         pre[key]=temp;
23         key=t;
24     }
25     return temp;
26 }
27 
28 void unite(int x,int y)
29 {
30     int rootx=find_pre(x);
31     int rooty=find_pre(y);
32     if(rootx!=rooty) pre[rootx]=rooty;
33 
34 }
35 int main()
36 {
37         cin >> m >> n;
38         int  all=m*n;
39         cin >> s;
40         init(all);
41         for(int i=1;i<=s;i++){
42         cin >> left1 >> right1;
43             unite(left1,right1);
44         }
45 
46         int att[MAX];
47         int num=0;
48         memset(att,0,sizeof(att));
49         for(int i=1;i<=all;i++){
50             att[find_pre(i)]=1;
51         }
52          for(int i=1;i<=all;i++){
53            if(att[i]==1) num++;
54         }
55         cout << num;
56     return 0;
57 }

 

 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////補充知識
  1. 0x3f3f3f3f的十進制是1061109567,也就是10^9級別的(和0x7fffffff一個數量級),而一般場合下的數據都是小於10^9的,所以它可以作為無窮大使用而不致出現數據大於無窮大的情形。
  1. 另一方面,由於一般的數據都不會大於10^9,所以當我們把無窮大加上一個數據時,它並不會溢出(這就滿足了“無窮大加一個有窮的數依然是無窮大”),事實上0x3f3f3f3f+0x3f3f3f3f=2122219134,這非常大但卻沒有超過32-bit int的表示范圍,所以0x3f3f3f3f還滿足了我們“無窮大加無窮大還是無窮大”的需求。
  1. 最后,0x3f3f3f3f還能給我們帶來一個意想不到的額外好處:如果我們想要將某個數組清零,我們通常會使用memset(a,0,sizeof(a))這樣的代碼來實現(方便而高效),但是當我們想將某個數組全部賦值為無窮大時(例如解決圖論問題時鄰接矩陣的初始化),就不能使用memset函數而得自己寫循環了(寫這些不重要的代碼真的很痛苦),我們知道這是因為memset是按字節操作的,它能夠對數組清零是因為0的每個字節都是0,現在好了,如果我們將無窮大設為0x3f3f3f3f,那么奇跡就發生了,0x3f3f3f3f的每個字節都是0x3f!所以要把一段內存全部置為無窮大,我們只需要memset(a,0x3f,sizeof(a))。

所以在通常的場合下,const int INF = 0x3f3f3f3f;真的是一個非常棒的選擇。

介紹一點關於0x3f3f3f的用法


免責聲明!

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



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