1. 程式人生 > >HDU 1232——暢通工程 並查集入門題

HDU 1232——暢通工程 並查集入門題

  • 題意 :給定你n個村莊以及m條路,每條路連線兩個村莊,求還需要最少多少條路使得任意兩個村莊之間互相可以到達(直接間接都可以)。
  • 思路 : 這是一道並查集經典的入門題,可以看這些村莊有多少個連通分量,然後連通分量的個數- 1即為答案 在這裡插入圖片描述 如圖所示,這是第一個樣例,節點1 3 4即為一個連通分量,2為一個連通分量,要想讓任意兩個村莊都能到達,只需要2 和 1 3 4任意一點連線即可。(圖中的方向只是為了表示1 到 3 的路和3 到1的路不同,不過走起來是沒有方向的。)
  • 程式碼 :
#include "bits/stdc++.h"
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
#define fori(i,l,u) for(int i = l;i < u;i++)
#define forj(j,l,u) for(int j = l;j < u;j++)
#define pb push_back
#define mk make_pair
#define F first
#define S second
typedef long long  ll;
typedef pair<string, int > ps;
typedef pair<int, int> pi;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef vector<pi> vpi;
const int maxn = 1e6 + 6;
ll N;
ll M;
ll u,v;
ll node[maxn];
ll ans = 0,visit[maxn];
ll find(ll x){              //尋找根節點座標,因為init裡初始 i = node[i]
    if (x == node[x]) {
        return x;
    }
    return find(node[x]);
}
void Unite(ll x,ll y){      //合併集合
    x = find(x);
    y = find(y);
    if (x == y) {
        return;
    }
    node[x] = y;
}
bool same(ll x,ll y){       //判斷是不是同一集合,判斷根節點的下標
    return find(x) == find(y);
}
void init(){                //初始化
    mem(visit,0);
    fori(i, 1, N+1){
        node[i] = i;
    }
}
//以上都是並查集的板子
int main()
{
//    freopen("1.txt", "r", stdin);
    while(~scanf("%lld%lld",&N,&M) && N){
        init();
        ans = 0;
        fori(i, 0, M){
            scanf("%lld%lld",&u,&v);
            if (!same(u, v)) {          //判斷是不是同一個連通分量
                Unite(u, v);
            }
        }
        fori(i, 1, N+1){
            if (node[i] == i) {         //判斷有多少個連通分量
                ans ++;
            }
        }
        ans -= 1;
        printf("%lld\n",ans);
    }
    return 0;
}


  • 遇到的問題 :這是第一道並查集的題,只需要把板子寫上,有些理解就能做出來。最後,一定要註釋掉 freopen!!!不然聽取WA聲一片