zoukankan      html  css  js  c++  java
  • [总结]单源最短路(朴素Dijkstra)与最小生成树(Prim,Kruskal)

    目录

    最短路

    朴素Dijkstra

    最小生成树

    Prim 算法

    Kruskal 算法




    最短路

    朴素Dijkstra

    时间复杂度:        O(n2+m) , n 表示点数,m 表示边数

    稠密图           存储形式:邻接矩阵

    模板: g[x][y]存的是初始化时边权, st[i]表示i是否加入正在逐步扩张的集合, dist[i] 存的是1到i的最短距离,也就是我们的目标

    思路:

    求1 → n 的最短路径

    1. 初始化距离 dist[1] = 0,  dist[i] = +∞

    2. for  i   1~n:

            for  j  1~n:

                    t  ← 不在集合s中的, 且距离集合最近的点

            s  ← t  (加入组织,即st[t]=true)

            for  j  1~n:

                    用 t 来更新其他点的的距离(已在集合中的点到1的距离可能不是最短,dist[j]取值更新为 min(dist[j], dist[t] + g[t][j]) )

    849. Dijkstra求最短路 I - AcWing题库高质量的算法题库https://www.acwing.com/problem/content/851/

    输入样例:

    3 3
    1 2 2
    2 3 1
    1 3 4
    

    输出样例:

    3

    #include <iostream>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    const int N = 510;
    int n, m;
    int dist[N],g[N][N];
    bool st[N]; //标记是否在集合中  
    
    int dijkstra()
    {
        memset(dist, 0x3f, sizeof dist);
        dist[1] = 0;
        
        for(int i = 0; i < n; i ++)
        {
            int t = -1;
            for(int j = 1; j <= n; j ++)
                if(!st[j] && (t==-1 || dist[j] < dist[t]))
                    t = j;
                    
            //if(t == n) break; //可以提前break, 优化时间开销 
            
            st[t] = true;
            for(int j = 1; j <= n; j ++)    //更新集合中的最短路径
                dist[j] = min(dist[j], dist[t]+g[t][j]);
            
        }
        
        if(dist[n] == 0x3f3f3f3f) return -1;
        else return dist[n];
    }
    int main()
    {
        cin >> n >> m;
        
        memset(g, 0x3f, sizeof g);
    
        int a,b,c;
        for(int i = 0; i < m; i++)
        {
            scanf("%d%d%d", &a, &b, &c);//边数较多,scanf省时
            g[a][b] = min(c, g[a][b]);  //由题意,图中可能存在重边和自环
        }
        
        cout << dijkstra();
    }

    最小生成树

    Prim 算法

    (与Dijkstra思想非常相似,只有更新dist时有区别,前者是到集合中与之距离最小的点,后者是到起始点的距离最小的点)

    思路:

    1. 初始化距离  dist[i] = +∞

    2. for  i   1~n:

            for  j  1~n:

                    t  ← 不在集合s中的, 且距离集合最近的点

            若i!=1(即不是初始时刻的点)且dist[t]==+∞(该图不连通), 直接退出(补充:只要不是第一个点,dist[t]就表示当前的点和已经连好的的集合里的某点之间的长度)

            for  j  1~n:

                    用 t 来更新其他点的的距离(已在集合中的点到1的距离可能不是最短,dist[j]取值更新为 min(dist[j],  g[t][j]) ) (补充:min(dist[j], g[t][j])中dist[j]表示之前当点t没有加入集合时 点j到集合的距离, g[t][j] 表示如果点t加入集合时 j到集合的距离)

    输入样例:

    4 5
    1 2 1
    1 3 2
    1 4 3
    2 3 2
    3 4 4
    

    输出样例:

    6

    #include <iostream>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    const int N = 510, INF = 0x3f3f3f3f;
    int n,m;
    int g[N][N],dist[N];
    bool st[N];
    
    int prim()
    {
        memset(dist, INF, sizeof dist);
        int res = 0;    //最小生成树的权值之和
        for(int i = 0; i < n; i++)
        {
            int t = -1;
            for(int j = 1; j <= n; j++)
                if(!st[j] && (t == -1 || dist[j] < dist[t]))
                    t = j;
                    
            if(i && dist[t]==INF) return INF;
            if(i) res += dist[t];//★,在更新之前加, 否则可能有自环的权会被算进来
            
            for(int j = 1; j <= n; j++)
                dist[j] = min(dist[j], g[t][j]);//★
            
            st[t] = true;//★
        }
        return res;
    }
    
    int main()
    {
        cin >> n >> m;
        
        memset(g, INF, sizeof g);
        
        int a, b, c;
        while (m -- )
        {
            scanf("%d%d%d", &a, &b, &c);
            g[a][b] = g[b][a] = min(c, g[a][b]); //★
        }
        
        int res = prim();
        if(res == INF) cout << "impossible";
        else cout << res;
        return 0;
    }

    Kruskal 算法

    (本质其实是并查集)

    思路:

    1. 所有边按权重从小到大排序

    2. 枚举每条边ab, 权重c

            如果a,b不连通,将此边加入集合

    输入样例:

    4 5
    1 2 1
    1 3 2
    1 4 3
    2 3 2
    3 4 4
    

    输出样例:

    6

    #include <iostream>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    const int N = 2e5+10, INF = 0x3f3f3f3f;
    int n ,m;
    int p[N];
    
    struct Edge{
        int a, b, w;
        
        bool operator < (const Edge& e)const
        {
            return w < e.w;
        }
    }e[N];
    
    int find(int x)    //并查集(压缩路径)
    {
        if(p[x]!=x) p[x] = find(p[x]);
        return p[x];
    }
    
    int kruskal()
    {
        sort(e, e+m);
        if(e[0].w == INF) return INF;
        
        for (int i = 0; i < n; i ++ ) p[i] = i;    //并查集初始化
        
        int res = 0, cntEdge = 0;
        
        for (int i = 0; i < m; i ++ )
        {
            int pa = find(e[i].a), pb = find(e[i].b);
            if(pa != pb){
                res += e[i].w;
                p[pa] = pb;
                cntEdge ++;
            }
        }
        
        if(cntEdge != n-1) return INF;
        else return res;
    }
    int main()
    {
        cin >> n >> m;
        
        int a, b, w;
        
        for (int i = 0; i < m; i ++ )
        {
            scanf("%d%d%d", &a, &b, &w);
            e[i] = {a, b, w};
        }
        
        int res = kruskal();
        
        if(res == INF) cout << "impossible";
        else cout << res;
        return 0;
    }

  • 相关阅读:
    纯css3实现的超炫checkbox复选框和radio单选框
    css3和jquery实现的可折叠导航菜单(适合手机网页)
    HTML5 Canvas 梦幻的文字飞扬动画教程
    纯css3实现的圆形旋转分享按钮
    纯css3实现的创意图片放大镜
    java.lang.ClassCastException: android.widget.RelativeLayout cannot be cast to android.widget.TextView
    python的range函数与切片操作符
    python简单基础代码
    android笔记 : Content provider内容提供器
    android笔记:Service
  • 原文地址:https://www.cnblogs.com/Knight02/p/15799052.html
Copyright © 2011-2022 走看看