AcWing 1144. 連接格點(最小生成樹)

題幹:

有一個 m 行 n 列的點陣,相鄰兩點可以相連。
一條縱向的連線花費一個單位,一條橫向的連線花費兩個單位。
某些點之間已經有連線了,試問至少還需要花費多少個單位才能使所有的點全部連通。
輸入格式
第一行輸入兩個正整數 m 和 n。
以下若干行每行四個正整數 x1,y1,x2,y2,表示第 x1 行第 y1 列的點和第 x2 行第 y2 列的點已經有連線。
輸入保證|x1−x2|+|y1−y2|=1
輸出格式
輸出使得連通所有點還需要的最小花費。

數據範圍
1≤m,n≤1000
0≤已經存在的連線數≤10000
輸入樣例:
2 2
1 1 2 1
輸出樣例:
3

思路:

題幹要求連通所有點的最小花費,很容易想到用最小生成樹,既然有些邊是必選的,那我們可以先把必選的邊放到生成樹中,然後遍歷所有的邊,直到成爲一個連通圖。
然後考慮最少花費,因爲縱向邊花費1,橫向邊花費2;所以先遍歷縱向邊,再遍歷橫向邊。
既然有部分邊已經給定,那生成樹中就可能出現環的情況,這樣我們的並查集就需要初始化成x了,初始化成-1會導致無限遞歸。。。
例:3 2
1 1 1 2
1 1 2 1
2 2 1 2
2 2 2 1
這裏的F[1]=F[2]=F[3]=F[4]=3,如果初始化-1會導致找不到頭結點。。。。

//瘋狂段錯誤。。。。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
int F[2100000];
int find(int x){
    if(F[x]==x)
        return x;
    return F[x]=find(F[x]);
    //因爲出現環了。。導致F[x]=find(F[x])無限進行。。。
}
int main(){
    int x,y,n,m,x1,x2,y1,y2;
    scanf("%d%d",&n,&m);

    for(int i=0;i<=n*m;i++) //答應我,別初始化成-1.。
        F[i]=i;

    while(cin>>x1>>y1>>x2>>y2){
        x=find((x1-1)*m+y1);
        y=find((x2-1)*m+y2);
        F[x]=y;
    }  

    int ans=0; 
    for(int i=1;i<n;i++){
        for(int j=1;j<=m;j++){
            x=find((i-1)*m+j);
            y=find(i*m+j);
            if(x!=y){
                F[x]=y;
                ans++;
            }
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<m;j++){
            x=find((i-1)*m+j);
            y=find((i-1)*m+j+1);
            if(x!=y){
                ans+=2;
                F[x]=y;
            }
        }
    }
    printf("%d\n",ans);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章