最小生成树

  这篇博客是关于图论中的最小生成树,在这里我们先简单介绍最小生成树的概念:一个图中,选取总代价最小的边使得所有点都连通。由此得到的结果必然是一棵树,同时需要注意一个图的最小生成树不具有唯一性。下面介绍求最小生成树的一种方法:普里姆算法求最小生成树。

  普里姆算法正确性可以使用反证法进行证明,这里不进行展开。

  普里姆算法的核心为以下步骤:

  (1)使用一个d数组用于存储各个节点到树的距离,初始化d数组为无穷大。使用一个book数组用于区分某个节点是树节点还是非树节点,book数组初始化为false。首先从任意一个顶点开始构造树,按照习惯从第1号节点开始构造树,将d[1] = 0,book[1] = true。

  (2)更新与1号节点相连的非树节点到树的距离,也就是更新d数组,更新条件为:如果i节点到树的距离比d[i]中存储的距离小,那么更新d[i]为更小的值。

  (3)遍历更新后的d数组,找到非树节点中距离树最小的节点,将这个节点加入树中。

  (4)重复步骤1、2、3,直到生成树中有n个节点为止。

  下面为实现代码:

  

#include <iostream>
#include <cstdio>
using namespace std;

const int MAXN = 5001;
const int MAXM = 20001;

struct Line {
    int p;
    int next;
    int v;
};

struct Node {
    int dis;
    bool book;
};

Line line[MAXM * 2];
Node node[MAXN];
int h[MAXN];

int n, m, x, y, v, now, ans;

void add(int x, int y, int v, int lID) {
    line[lID].p = y;
    line[lID].v = v;
    line[lID].next = h[x];
    h[x] = lID;
}

int find() {
    int k = 0;
    for(int i = 1; i <= n; i++) {
        if(!node[i].book) {
            if(k == 0 || node[i].dis < node[k].dis) {
                k = i;
            }
        }
    }
    return k;
}

void update(int now) {
    int k = h[now];
    int p;
    while(k) {
        p = line[k].p;
        if(!node[p].book) {
            if(node[p].dis > line[k].v) {
                node[p].dis = line[k].v;
            }
        }
        k = line[k].next;
    }
}

int main() {
    scanf("%d %d", &n, &m);
    for(int i = 1; i <= m; i++) {
        scanf("%d %d %d", &x, &y, &v);
        add(x, y, v, 2 * i - 1);
        add(y, x, v, 2 * i);
    }
    for(int i = 1; i <= n; i++) {
        node[i].book = false;
        node[i].dis = (1 << 31) - 1;
    }

    node[1].book = true;
    node[1].dis = 0;
    ans = 0;
    update(1);

    for(int i = 1; i < n; i++) {
        now = find();
        node[now].book = true;
        ans += node[now].dis;
        update(now);
    }
    printf("%d", ans);
    return 0;
}

 

 

   圆满结束。

posted @ 2018-08-02 17:59  potato226  阅读(180)  评论(0编辑  收藏  举报