[Baekjoon,1197] 최소 스패닝 트리

1 분 소요

간선의 가중치 합이 최소인 스패닝 트리를 만들어야 한다. 이를 위해서 나는 크루스칼의 알고리즘을 이용했다. 크루스칼 알고리즘은 아주 간단하다. 선택 후 사이클이 되지 않는 간선을 선택한다. 이때 간선의 가중치가 작은 것부터 차례로 선택한다. 이를 최소 스패닝 트리가 될 때까지 반복한다.

여기서 중요한 것은 사이클이 되지 않는 간선만을 선택을 하는 것이다. 이는 상호 배타적 집합 자료구조를 이용하면 간단하다. 두 정점이 이미 같은 집합에 속해있다면 이들을 이을 경우 사이클이 된다. 따라서 두 정점이 다른 집합에 있는 경우에만 이어나가면 된다.

#include <iostream>
#include <vector>
#include <algorithm>
#include <climits>
using namespace std;

const int MAX_V=10000;
const int INF=INT_MAX;

struct DisjointSet
{
    vector<int> parent,rank;
    DisjointSet(int v) : parent(v), rank(v,1)
    {
        for(int i=0;i<v;++i)
            parent[i]=i;
    }
    // u가 속한 집합을 찾는다. 
    int find(int u)
    {
        if(parent[u]==u)
            return u;
        return parent[u]=find(parent[u]);
    }
    bool merge(int u, int v)
    {
        u=find(u); v=find(v);
        if(u==v) return false;
        // u의 트리가 rank가 더 깊게 한다. 
        if(rank[u]<rank[v]) swap(u,v);
        parent[u]=v;
        // 두 트리의 rank가 같은 경우 
        if(rank[u]==rank[v])
            ++rank[u];
        return true;
    }
};

int V,E;
vector<pair<int,int> > adj[MAX_V];

int getMinWeight()
{
    // <간선의 가중치, 간선 인덱스>
    vector<pair<int,pair<int,int> > > edges;
    for(int here=0;here<V;++here)
        for(int i=0;i<adj[here].size();++i)
        {
            int there=adj[here][i].first;
            int cost=adj[here][i].second;
            edges.push_back(make_pair(cost,make_pair(here,there)));
        }
    sort(edges.begin(),edges.end());
    
    DisjointSet set(V);
    int ret=0;
    for(int i=0;i<edges.size();++i)
    {
        int cost=edges[i].first;
        int u=edges[i].second.first;
        int v=edges[i].second.second;
        if(set.merge(u,v))
            ret+=cost;
    }
    return ret;
}

int main()
{
    cin>>V>>E;
    for(int i=0;i<E;++i)
    {
        int a,b,c;
        scanf("%d %d %d",&a,&b,&c);
        --a;--b;
        adj[a].push_back(make_pair(b,c));
    }
    cout<<getMinWeight()<<endl;
    return 0;
}

댓글남기기