並查集深度應用

qq_39304630發表於2018-09-01

先來看一道題目:島嶼

現在給你一個m*n矩陣,1表示陸地,0表示海,現在要你統計出這個矩陣上有幾個小島,上下左右聯通的元素屬於一座島。

1 1 0 0 1 

1 0 1 1 1 

0 1 0 0 0 

上例就應該是3。

這題:很明顯是在一個圖上求聯通分量,但是比聯通分量還簡單。

思路:

A.BFS

1.從點(1,1)開始,進行BFS,將遍歷到的點都標記為2,結束一輪BFS,島嶼數量+1

2.遍歷下一個節點,如果已經被標記為2,則直接跳過

複雜度分析:就是遍歷所有為1的節點的複雜度,雖然遍歷矩陣有兩層迴圈,但是實際上的BFS只遍歷了為1的點而已,O(n*m)。

#include<cstdio>
#include<queue> 
using namespace std;
int Map[105][105],total,m,n;

typedef struct{
    int x,y;
}Point;

void BFS(int x,int y){
    queue<Point>que;
    Point temp;
    int xx,yy;
    que.push(Point{x,y});
    while(!que.empty()){
        temp=que.front();
        que.pop();
        xx=temp.x;
        yy=temp.y;
        Map[xx][yy]=2;
        if(xx>1&&(Map[xx-1][yy]&1)){
            que.push(Point{xx-1,yy});
        }
        if(xx<n&&(Map[xx+1][yy]&1)){
            que.push(Point{xx+1,yy});
        }
        if(yy>1&&(Map[xx][yy-1]&1)){
            que.push(Point{xx,yy-1});
        }
        if(yy<n&&(Map[xx][yy+1]&1)){
            que.push(Point{xx,yy+1});
        }
    }
}

int main(){

    scanf("%d%d",&m,&n);
    for(int i=1;i<=m;i++){
        for(int j=1;j<=n;j++){
            scanf("%d",&Map[i][j]);
        }
    }
    for(int i=1;i<=m;i++){
        for(int j=1;j<=n;j++){
            if(Map[i][j]&1){
                BFS(i,j);
                total++;
            } 
        }
    } 
    printf("島嶼數量:%d",total);
    return 0;

 

B.DFS  這是左神的思路:

實現一個infect(i,j)函式,這個函式會將(i,j)周圍的點感染為2,同時,被感染的點遞迴地去感染他們周圍的點。

#include<cstdio>
using namespace std;
int Map[105][105],total,m,n;
void infect(int x,int y){
    if(x<=0||x>m||y<=0||y>n||(Map[x][y]!=1)){
        return;
    }
    Map[x][y]=2;
    infect(x-1,y);
    infect(x+1,y);
    infect(x,y-1);
    infect(x,y+1);
}

int main(){
    scanf("%d%d",&m,&n);
    for(int i=1;i<=m;i++){
        for(int j=1;j<=n;j++){
            scanf("%d",&Map[i][j]);
        }
    }
    for(int i=1;i<=m;i++){
        for(int j=1;j<=n;j++){
            if(Map[i][j]&1){
                infect(i,j);
                total++;
            } 
        }
    } 
    printf("島嶼數量:%d",total);
    return 0;
}

左神思路分析:其實與BFS差不了多少,遍歷的點的個數也基本相同,相對於BFS來說程式碼精簡,缺陷就是:壓棧和還原環境代價

 

改:如果現在這個矩陣十分大,使得你只能平行計算,求解。

平行計算的困難:當各個分塊求出來之後,可能存在那種小塊中分離,大塊中合併的聯通塊。 

如圖:A和B可能是屬於同一個聯通塊的,但是平行計算中,由於劃分,左邊塊和右邊塊各自有兩個聯通塊,所以給小塊合併帶來了困難。

分析:對於處在邊界的聯通塊要做特殊處理,所以我們需要這兩塊的分界線的邊界資訊。

以上圖為例說明,左塊有兩個,右塊有兩個,當左右合併時,A、B是聯通的,所以可以把他們合併,由於合併一次,就會減少一個聯通塊,所以是三個。

以矩陣為例做分析:

1 1 1 1 1 1 1 1 1 1 

0 1 1 1 1 1 1 0 0 0 

0 0 0 0 0 0 1 0 0 0 

0 0 1 0 0 1 1 0 1 1 

我們將這個矩陣分兩半,

   左部                右部

1 1 1 1 1         1 1 1 1 1

0 1 1 1 1         1 1 0 0 0

0 0 0 0 0         0 1 0 0 0

0 0 1 0 0         1 1 0 1 1

我們將各個聯通塊標記出來:

   左部                右部

1 1 1 1 1         3 3 3 3 3

0 1 1 1 1         3 3 0 0 0

0 0 0 0 0         0 3 0 0 0

0 0 2 2 2        3 3 0 4 4

所以分開求有4個,我們將兩條邊界線單獨拿出來:

左邊     右邊

   1           3

   1           3

   0           0

   2           3

第一行,1和3挨著且不屬於一個集合,所以1和3集合合併,總數量-1變為3

第二行,1和3已經屬於一個集合,往下

第三行,0和0,不連通,往下

第四行:2和3挨著且不屬於一個集合,所以2和3集合合併,總數量-1變為2

結束

所以總的數量為2

相關文章