• 最小生成树系列


    背景:

    What is the minimum spanning tree? 
    Given a connected , undirected graph, a spnning tree without circle of that graph is a subgraph that is a tree and connects all the vertices together. A single graph an have more than one spanning tree.If we assign a weight to each edge, then the weight of a spanning tree is the sum of the weights of the edges in the spanning tree.A minmum spanning tree(MST) is a spanning tree with weight less than or equal to the weight of every other spanning tree.

    相关算法(Kruskal algorithm)

    Introduction:
    Kruskal algorithm, regard edge as object(以边为对象), finds a subset of the edges that forms a tree that includes every vertex, where the total weight of all the edges in the tree is minimized. It is a greedy algorithm in graph theory as it finds a minmum spanning tree for a connceted weighted graph at each step.
    
    Description:
    1)create a forest F (a set of trees), where each vertex in the graph is a separate tree
    2)create a set S containing all the edges in the graph
    3) while S is nonempty and F is not yet spanning
      remove an edge with minimum weight from S
      if the removed edge connects two different trees then add it to the forest F, combining two trees into a single tree
    At the termination of the algorithm, the forest forms a minimum spanning forest of the graph. If the graph is connected, the forest has a single component and forms a minimum spanning tree.

    Example:(图片来自维基) 排序后,找最小边权的边AD(1) (2) (3) (4) (5) (6)
    Pseudocode: Kruskal(G):   A
    = ∅   foreach v ∈ G.V:    MAKE-SET(v)   foreach (u, v) ordered by weight(u, v), increasing:     if FIND-SET(u) ≠ FIND-SET(v):       A = A ∪ {(u, v)}     UNION(u, v)   return A

    Code:
    #include<iostream>
    #include<algorithm>
    #define MAX 100
    
    using std::endl;
    using std::cout;
    using std::cin;
    
    
    int parent[MAX];
    int child[MAX];
    
    struct Edge {
        int start, end;
        int weight;
    };
    
    bool comp(Edge a, Edge b) {
        return a.weight < b.weight;
    }
    
    int find(int child) {
        return child == parent[child] ? child : find(parent[child]);
    }
    
    bool join(int first, int second) {
        int root1, root2;
    
        root1 = find(first);
        root2 = find(second);
    
        if(root1 == root2)
            return false;
        else {
            if(child[root1] >= child[root2]){
                parent[root2]  = root1;
                child[root1] += child[root2];
            }
               else {
                parent[root1] = root2;
                child[root2] += child[root1];
            }
        }
        return true;
    }
    int main() {
        int vertex_num, edge_num, i, sum, total;
           cin >> vertex_num >> edge_num;
        Edge edge[edge_num + 1]; // edge[0] is not used
        //init
        for(i = 0; i < vertex_num; ++i) {    // Make-set操作,给每一个结点建立一棵树(一个集合),这就形成每个点成为单独的树(集合),构成森林。
            parent[i] = i;
            child[i] = 1;
        }
      
    for(i = 1; i <= edge_num; ++i) { cin >> edge[i].start >> edge[i].end >> edge[i].weight;} std::sort(edge + 1, edge + 1 + edge_num, comp); //排序,主要是为后面的贪婪算法, for(sum = 0, total = 0, i = 1; i <= edge_num; ++i) { //从边权小的往边权大的边一次遍历就可以了,每次找到边权最小的,且与已有的spanning tree不构成环 if(join(edge[i].start, edge[i].end)) { sum += edge[i].weight; ++total; cout << "Add the edge: " << edge[i].start << " -> " << edge[i].end << " : "<< edge[i].weight << endl; } if(total == vertex_num - 1) break; } cout << "The sum of the mst is " << sum << endl; }

    代码说明:上述代码使用了并查集来进行优化Kruskal算法,利用并查集我们可以很方便的进行两个不相交集合之间的查询与合并操作,在MST问题之间,不相交集合就是指子树与子树之间。

        如果需要更好的了解并查集的操作和实现,可以看并查集

    相关算法(Prime)

    算法基本思想描述:
    1)生成空的生成树S,取一个图G中的顶点v加入到生成树中, 即G = G - {v}, S = S + {v}
    2)遍历G,找出其中可与S中的顶点组成边权最小的顶点,并将这个顶点加入到S中,并从G中删除,将打到的边加到MST中;
    3)重复步骤2,直到所有的顶点都进入了S中为止,此时得到的MST就是最小MST

    例子:
           。。。。 
    以上只给出三个过程图,因为一共有7个点,所以一共要遍历7次,过程都一样。

    代码:
    #include<iostream>
    #include<cstring>
    #define MAX 0x3f3f3f3f
    
    
    using std::cout;
    using std::endl;
    using std::cin;
    
    int Adjacency_Matric[100][100];
    int pre[100];  //用于输出用的,保存每个
    
    void Prim(int vertex_num) {
        int lowcost[vertex_num];//用于保存图中的顶点到S中的顶点的最小的距离;
       int i, sum = 0, min, min_vertex, j; bool visit[vertex_num]; //ini memset(pre, 0, sizeof(pre)); for(i = 0; i < vertex_num; ++i) { lowcost[i] = Adjacency_Matric[0][i]; visit[i] = false; } visit[0] = true; //prim for(j = 1; j < vertex_num; ++j) { //循环的次数,最多就是顶点的个数,因为加入到mst中的顶点最多也就这么多个。 min = MAX, min_vertex = 0; for(i = 0; i < vertex_num; ++i) { if(!visit[i] && lowcost[i] < min) { min = lowcost[i]; min_vertex = i; } } //upgrade the lowcost according the new mst's vertex min_vertex; visit[min_vertex] = 1; sum += min; for(i = 0; i < vertex_num; ++i) { if(lowcost[i] > Adjacency_Matric[min_vertex][i]) { lowcost[i] = Adjacency_Matric[min_vertex][i]; pre[i] = min_vertex; } } cout << pre[min_vertex] << " -> " << min_vertex << " with the weight : " << min << endl; } cout << "The mst's cost is " << sum << endl; } int main() { int vertex_num, edge_num, i, first, second, weight; //init memset(Adjacency_Matric, MAX, sizeof(Adjacency_Matric)); cin >> vertex_num >> edge_num; //input the information of the edge for(i = 0; i < edge_num; ++i){ cin >> first >> second >> weight; Adjacency_Matric[first][second] = Adjacency_Matric[second][first] = weight; } Prim(vertex_num); }

    参考网址:

    MST介绍
    http://en.wikipedia.org/wiki/Minimum_spanning_tree

    Krustkal介绍
    http://en.wikipedia.org/wiki/Kruskal%27s_algorithm

    并查集介绍
    http://blog.csdn.net/dellaserss/article/details/7724401
  • 相关阅读:
    POJ1182 食物链---(经典种类并查集)
    HDU1025---(LIS 最长上升子序列 的应用)
    HDU5748---(记录每个元素的 最长上升子序列 nlogn)
    c++ 批量初始化数组 fill和fill_n函数的应用
    JNI 方法注册与签名+BufferedReader使用readLine问题
    正确在遍历中删除List元素
    Head First Java设计模式思维导图总结
    关于一些基础的Java问题的解答(七)
    关于一些基础的Java问题的解答(六)
    关于一些基础的Java问题的解答(四)
  • 原文地址:https://www.cnblogs.com/kinthon/p/4509255.html
Copyright © 2020-2023  润新知