最小生成树

一个图的最小生成树是原图的极小连通子图,包含原图中的所有顶点,并且有保持图连通的最少的边。最小生成树一般用Kruskal算法或Prim算法求。

Kruskal算法

思路是先将图中所有边按权值从小到大排序,然后依次遍历各条边,如果该边的两个顶点不连通,则选用该边构成最小生成树,否则丢弃。由于边接n个顶点只需要n-1条边,循环可以提前终止。点之间是否连通可以用并查集来维护。

例题:求最小生成树

int f[100005];
struct Edge {
    int s, t, c;
    bool operator<(const Edge &o) const {
        return c < o.c;
    }
}e[1000005];
int root(int x) {
    return x == f[x] ? x : f[x] = root(f[x]);
}
int connected(int x, int y) {
    return root(x) == root(y);
}
void merge(int x, int y) {
    f[root(x)] = root(y);
}
int main() {
    int n, m, i, j, ans;
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) f[i] = i;
    for (i = 0; i < m; i++)
        scanf("%d%d%d", &e[i].s, &e[i].t, &e[i].c);
    sort(e, e + m);
    for (ans = i = j = 0; i < m && j < n - 1; i++) {
        if (!connected(e[i].s, e[i].t)) {
            j += 1;
            ans += e[i].c;
            merge(e[i].s, e[i].t);
        }
    }
    printf("%d\n", ans);
    return 0;
}

Prim算法

Prim算法的策略以及代码实现与Dijkstra算法几乎是相同的,唯一的区别在于Dijkstra算法的点在入堆时用的是源点到该点的距离,而Prim算法中用的是边权。

int n, m, a, b, c, v[10005], ans;
vector<pair<int,int> > g[10005];
struct ST {
    int idx, dst;
    bool operator<(const ST &o) const {
        return dst > o.dst;
    }
};
priority_queue<ST> pq;
int main() {
    scanf("%d%d", &n, &m);
    for (int i = 0; i < m; i++) {
        scanf("%d%d%d", &a, &b, &c);
        g[a].push_back(make_pair(b, c));
        g[b].push_back(make_pair(a, c));
    }
    pq.push((ST){a, 0});
    while (!pq.empty()) {
        ST t = pq.top(); pq.pop();
        if (v[t.idx]) continue;
        v[t.idx] = 1; ans += t.dst;
        for (size_t i = 0; i < g[t.idx].size(); i++) {
            pair<int,int> j = g[t.idx][i];
            if (!v[j.first])
                pq.push((ST){j.first, j.second});
        }
    }
    printf("%d\n", ans);
    return 0;
}

说明

Table of Contents