Graph theory chapter 2-- minimum spanning tree algorithm

basic concepts

Tree (Tree)

If the loop does not exist without a connected graph, then this is called the tree of FIG.

Spanning Tree  (Spanning Tree)

A sub-tree of all vertices to FIG no connected graph G comprising if one of G, called the subgraph of G spanning tree.

Minimal spanning tree is connected subgraphs of the connectivity graph. Here, the term refers to the minimum: an edge if any increase in the tree, a loop will occur; if one side removed, so will not communicate with FIG.

Minimum spanning tree

A weighted value of communicating FIG . With $ n-1 $ $ n-$ edges to vertices connected, and connecting the minimum weight .

Scenarios

Imagine 9 villages, which constitute a location shown in the following figure, the straight line is different from each village. To set up the network cable between each village, to ensure a minimum cost, the need to be able to select a link 9 villages, and the minimum length of the route.

 

Kruskal's algorithm

Knowledge Point: Data Structure - disjoint-set

The basic idea

Always select currently available, will not be (and have been selected side) to form a loop minimum planting right side.

Specific steps:

1. all sides by weight for descending sort

2. Select side minimum weight

3. If the two vertices fall on different sides of the connected components, selecting this edge, and these two vertexes of the same marker connected components; if the two vertices of this edge falls the same connected component, discard this edge. 2,3 repeated until all the components are communicating on the same. [This step need to use the above disjoint-set]

Template question: https://www.luogu.org/problem/P3366

#include <iostream>
#include <algorithm>
#include <fstream>
using namespace std;
int pre[5005];
int n, m; //n个定点,m条边

struct ENode {
    int from, to, dis;
    bool operator<(ENode p) {
        return dis < p.dis;
    }
}M[200005];

void initialize() {
    for (int i = 0; i <= 5000; i++) {
        pre[i] = i;
    }
}

int Find(int x) {
    return x == pre[x] ? pre[x] : pre[x] = Find(pre[x]);
}

int kurskal() {
    int N = n, res = 0;
    initialize();
    for (int i = 0; i < m && N > 1; i++) {
        int fx = Find(M[i].from), fy = Find(M[i].to);
        if (fx != fy) {
            pre[fx] = fy;
            N-; // find an edge, when N 1 is reduced to N-1 indicates that have found the edges, complete the 
            RES + = M [I] .dis; 
        } 
    } 
    IF (N == 1 ) / / cycle done, N is not equal to 1 indicates that no suitable N-1 edges form the minimum spanning tree to 
        return RES;
     return - 1 ; 
} 

int main () { 
#ifdef the LOCAL 
    the fstream CIN ( " data.in. " );
 #endif  // the LOCAL 
    CIN >> >> n- m;
     for ( int I = 0 ; I <m; I ++ ) { 
        CIN >> m [I]. from >> M[i].to >> M[i].dis;
    }
    sort(M, M + m);
    int ans = kurskal();
    if (ans != -1)
        cout << ans << endl;
    else
        cout << "orz" << endl;
    return 0;
}

Prim's algorithm

Prim's algorithm idea :

First, FIG point divided into two parts, one is visited U $ $ (optionally first edge), one is not visited V $ $

1: Every time looking for $ u $ to $ v $ smallest edge weights.

2: these edges and vertices in V $ $ $ was added to U $ until V $ $ $ = number of edges in the number of vertices $ $ $ -1

Illustrates the steps of:

Maintain a $ dis $ array, recording only the use has access node can reach each unvisited node minimum weight.

The initial value of the node 1 (may be any of a) to a value of each point, provision is 0 to themselves, not to the INF $ $ (defined in a particularly large number).

Find the current value of the right to reach the shortest point. 1--> 4, node 4

 

The dis [4] assigned to 0, marked as visited , and updated by the node 4 dis array.

 Turn back

 

 

Finally, the entire array dis is 0, the minimum spanning tree will come out, if dis $ $ $ inf $ array as well, it indicates that this is not a connected graph.

 Or the above template questions that road: https://www.luogu.org/problem/P3366

#include <iostream>
#include <fstream>
using namespace std;
typedef long long LL;

struct ENode {
    int dis, to;//权重、指向
    ENode* next = NULL;
    void push(int to, int dis) {
        ENode* p = new ENode;
        p->to = to; p->dis = dis;
        p->next = next;
        next = p;
    }
}*head;
const int inf = 1 << 30;
int n, m; 
int dis[5005];

int prim() {
    int res = 0;
    
    dis[1] = 0;
    for (int i = 2; i <= n; i++) {
        dis[i] = inf;
    }

    ENode* p = head[1].next;
    while (p) {
        if (p->dis < dis[p->to])//看了题目数据,有重边
        dis[p->to] = p->dis;
        p = p->next;
    }
    
    for (int i = 1; i < n; i++) {
        int v,MIN = inf;
        for (int j = 1; j <= n; j++) {
            //到不了的,访问过的不进行比较
            if (dis[j] != 0 && dis[j] < MIN) {
                v = j;
                MIN = dis[j];
            }
        }
        if (MIN == inf)
            return -1;//没找够M-1条线,就没路了
        res += dis[v];
        dis[v] = 0;
        p = head[v].next;
        while (p) {
            if (dis[p->to] > p->dis) {
                dis[p->to] = p->dis;
            }
            p = p->next;
        }
    }
    return res;
}

int main() {
#ifdef LOCAL
    fstream cin("data.in");
#endif // LOCAL
    cin >> n >> m;
    head = new ENode[n + 1];
    for (int i = 0; i < m; i++) {
        int from, to, dis;
        cin >> from >> to >> dis;
        head[from].push(to, dis);
        head[to].push(from, dis);
    }
    int ans = prim();
    if (ans != -1)
        cout << ans << endl;
    else
        cout << "orz" << endl;
    return 0;
}

两者区别

时间复杂度

prim算法

时间复杂度为$O(n^2)$,$n$为顶点的数量,其时间复杂度与边得数目无关,适合稠密图。

kruskal算法

时间复杂度为$O(e\cdot loge)$,$e$为边的数目,与顶点数量无关,适合稀疏图。其实就是排序的时间,因为并查集的查询操作时$O(1)$,如果建堆的话会更快一些,自下而上建堆复杂度是$O(n)$,最好自己实现,用priority_queue试了一下,为啥更慢了

通俗点说就是,点多边少用Kruskal,因为Kruskal算法每次查找最短的边。 点少边多用Prim,因为它是每次找一个顶点。

具体选择用那个,可以用电脑算一下,题目给的数据级别,$n^2$和$e\cdot loge$看看那个小,比如上面的模板题,题目给的数据级别是$(n<=5000,e<=200000)$,粗略估算一下,kurskal算法一定是会快不少的。

kurskal算法

prim算法

实现难度

明眼人都能看出来,kurskal算法要简单太多了。kurskal算法不需要把图表示出来,而Prim算法必须建表或者邻接矩阵,所以从上面的数据也能看出来当边的数目较大时,Prim算法所占用的空间比kurskal算法多了很多。

拓展

堆优化Prim算法

百度搜索:prim堆优化 

使用堆优化后的Prim算法时间复杂度为$O(nlogn)$。

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#define R register int
using namespace std;

int k,n,m,cnt,sum,ai,bi,ci,head[5005],dis[5005],vis[5005];

struct Edge
{
    int v,w,next;
}e[400005];

void add(int u,int v,int w)
{
    e[++k].v=v;
    e[k].w=w;
    e[k].next=head[u];
    head[u]=k;
}

typedef pair <int,int> pii;
priority_queue <pii,vector<pii>,greater<pii> > q;

void prim()
{
    dis[1]=0;
    q.push(make_pair(0,1));
    while(!q.empty()&&cnt<n)
    {
        int d=q.top().first,u=q.top().second;
        q.pop();
        if(vis[u]) continue;
        cnt++;
        sum+=d;
        vis[u]=1;
        for(R i=head[u];i!=-1;i=e[i].next)
            if(e[i].w<dis[e[i].v])
                dis[e[i].v]=e[i].w,q.push(make_pair(dis[e[i].v],e[i].v));
    }
}

int main()
{
    memset(dis,127,sizeof(dis));
    memset(head,-1,sizeof(head));
    scanf("%d%d",&n,&m);
    for(R i=1;i<=m;i++)
    {
        scanf("%d%d%d",&ai,&bi,&ci);
        add(ai,bi,ci);
        add(bi,ai,ci);
    }
    prim();
    if (cnt==n)printf("%d",sum);
    else printf("orz");
}

 

Guess you like

Origin www.cnblogs.com/czc1999/p/11735791.html