• 最小生成树详解


    注:本文算法使用链式前向星数据结构实现。学习链接:链式前向星-学习笔记

    一、Prim算法

    普通prim算法模板:

    //用前向星录数据的时候记得把head初始化为-1 
    fill(dist,dist+LEN,MAX);
    memset(vis,0,sizeof vis);
    int ans=0;
    dist[1]=0;        //如果题目要求输出最小生成树,就把题目要求的源点s的dist设为0 
    while(1){        //如果题目要求判断最小生成树是否能覆盖所有边,这个循环条件应该是i=n;while(n--)循环n次。 
        int u=-1,d=MAX;
        for(i=1;i<=N;i++){
            if(!vis[i] && dist[i]<d){
                u=i;
                d=dist[i];
            }
        }
        if(u<0) break;    //如果题目要求判断最小生成树是否能覆盖所有边,出现这样的情况说明不能覆盖所有边。 
        vis[u]=1;
        ans+=dist[u];
        for(i=head[u];~i;i=mp[i].next){    //用前向星遍历u点所有的后继。i是各个后继点在mp的下标,mp[to]是u的各个后继点 
            int to=mp[i].to;
            if(!vis[to] && mp[i].w<dist[to]){//如果这个点没有被访问过、并且u->v的路径比点集S到v的路径要短,则更新。 
                dist[to]=mp[i].w;
            }
        }
    }
    O("%d
    ",ans);

    堆优化的prim算法:

    堆结构:

    struct cmp{
        bool operator () (int a,int b){
            return dist[a]>dist[b];
        }
    }; 
    priority_queue<int,vector<int>,cmp> pq;

    算法代码:

    int ans=0;
    dist[1]=0;
    pq.push(1);
    while(!pq.empty()){
        int u=pq.top();
        pq.pop();
        if(vis[u]) continue;
        vis[u]=1;
        ans+=dist[u];
        for(i=head[u];~i;i=mp[i].next){
            int to=mp[i].to;
            if(!vis[to] && mp[i].w<dist[to]){
                dist[to]=mp[i].w;
                pq.push(to);
            }
        }
    }
    O("%d
    ",ans);

    二、Kruskal算法

    1.建立边表数据结构

    typedef struct edge{
        int u,v,w;
        edge(int u=0,int v=0,int w=0):u(u),v(v),w(w)
        {
        }
        bool operator < (const edge& obj) const
        {
            return w<obj.w;
        }
    }edge;
    edge mp[LEN*LEN];

    2.编写并查集模板(以下代码没有写合并的Union操作。这个操作在主代码执行的时候已经实现)

    int fa[LEN];
    int init(){
        int i;
        FF(i,LEN) fa[i]=i;
    }
    int findFa(int x){
        if(x==fa[x]) return x;
        int r=x;
        while(r!=fa[r]){
            r=fa[r];
        }
        int t=x;
        while(x!=fa[x]){
            t=fa[x];
            fa[x]=r;
            x=t;
        }
        return r;
    }

    3.编写主代码

    sort(mp,mp+cnt);
    FF(i,cnt){
        int fa_u=findFa(mp[i].u);
        int fa_v=findFa(mp[i].v);
        if(fa_u!=fa_v){
            ans+=mp[i].w;
            fa[fa_u]=fa_v;
            edge_cnt++;
            if(edge_cnt>=N-1) break;
        }
    }
    O("%d
    ",ans);

    注意:

    ①边表的范围要开大,因为边的数目可能是顶点数目的平方(准确说,有向图边树E=N*(N-1) )

    ②Prim算法在录边的数据的时候,因为是无向图,一条边要录成两条。Kruskal就没有这种必要了。

    ③各种初始化代码(比如并查集的init() )要注意加上。

    打个OJ测试一下吧!

    OJ链接:还是畅通工程

    AC代码:

    #include <stdio.h>
    #include <memory.h>
    #include <math.h>
    #include <string>
    #include <vector>
    #include <set>
    #include <stack>
    #include <queue>
    #include <algorithm>
    #include <map>
    
    #define I scanf
    #define OL puts
    #define O printf
    #define F(a,b,c) for(a=b;a<c;a++)
    #define FF(a,b) for(a=0;a<b;a++)
    #define FG(a,b) for(a=b-1;a>=0;a--)
    #define LEN 1010
    #define MAX (1<<30)-1
    #define V vector<int>
    
    using namespace std;
    
    int N;
    int fa[LEN];
    int init(){
        int i;
        FF(i,LEN) fa[i]=i;
    }
    int findFa(int x){
        if(x==fa[x]) return x;
        int r=x;
        while(r!=fa[r]){
            r=fa[r];
        }
        int t=x;
        while(x!=fa[x]){
            t=fa[x];
            fa[x]=r;
            x=t;
        }
        return r;
    }
    
    typedef struct edge{
        int u,v,w;
        edge(int u=0,int v=0,int w=0):u(u),v(v),w(w)
        {
        }
        bool operator < (const edge& obj) const
        {
            return w<obj.w;
        }
    }edge;
    edge mp[LEN*LEN];
    int cnt=0;
    
    int main(){
    //    freopen("还是畅通工程.txt","r",stdin);
        int i,j,u,v,w;
        while(scanf("%d",&N),N){
            init();
            cnt=0;
            int ans=0;
            int edge_cnt=0;
            i=(N*(N-1))/2;
            while(i--){
                I("%d%d%d",&u,&v,&w);
                mp[cnt++]=edge(u,v,w);
    //            mp[cnt++]=edge(v,u,w);
            }
            sort(mp,mp+cnt);
            FF(i,cnt){
                int fa_u=findFa(mp[i].u);
                int fa_v=findFa(mp[i].v);
                if(fa_u!=fa_v){
                    ans+=mp[i].w;
                    fa[fa_u]=fa_v;
                    edge_cnt++;
                    if(edge_cnt>=N-1) break;
                }
            }
            O("%d
    ",ans);
        }
        
        return 0;
    }
    View Code
  • 相关阅读:
    vue2.0 keep-alive最佳实践
    Vue解决安卓4.4不兼容的问题
    体验异步的终极解决方案-ES7的Async/Await
    axios在vue中的简单配置与使用
    AngularJS-UI-Router
    SignalR 填坑记
    小心C# 5.0 中的await and async模式造成的死锁
    使用BCP导出导入数据
    关闭正在执行的事务 Kill
    C# 正则表达式
  • 原文地址:https://www.cnblogs.com/TQCAI/p/8549353.html
Copyright © 2020-2023  润新知