• 最小生成树的kruskal、prim算法


    kruskal算法和prim算法

    都说 kruskal是加边法,prim是加点法

    这篇解释也不错:这篇

    1、kruskal算法

      因为是加边法,所以这个方法比较合适稀疏图。要码这个需要先懂并查集。因为我不会画好看的图,所以看不懂的话推荐下面博客的说明。这里是下面

    步骤:

    1、把每个点都看成一棵树,那么你就会得到一片森林。。。

    2、把每棵树之间的距离从小到大排序,即把边排序

    3、从小到大取边,把不在同一棵树的点连通到同一棵树上。。。最后你会得到一棵树,就是最小生成树

     我去盗图。。。这里的图

    给个模板题吧。。HDU1233

    #include <iostream>
    #include <cstdio>
    #include <string>
    #include <cstring>
    #include <cmath>
    #include <sstream>
    #include <algorithm>
    #include <set>
    #include <map>
    #include <vector>
    #include <queue>
    #include <iomanip>
    #include <stack>
    
    using namespace std;
    
    typedef long long LL;
    const int INF = 0x3f3f3f3f;
    const int MAXN = 5005;
    const int MOD = 1e9 + 7;
    
    #define MemI(x) memset(x, -1, sizeof(x))
    #define Mem0(x) memset(x, 0, sizeof(x))
    #define MemM(x) memset(x, 0x3f, sizeof(x))
    
    struct Edge
    {
        int u, v, w;
        bool operator < (const Edge &a) const
        {
            return w < a.w;
        }
    }edge[MAXN];
    int n, ans, fa[105];
    
    //找父节点并压缩路径,这是并查集的知识
    int Get_f(int x)
    {
        if(fa[x] == x)
            return x;
        return fa[x] = Get_f(fa[x]);
    }
    
    void Kruskal()
    {
        //初始化
        for(int i = 1;i <= n;++i)
            fa[i] = i;
        sort(edge, edge + (n - 1) * n / 2);
        ans = 0;
        
        int x, y, cnt = 1;
        for(int i = 0;i < n * (n - 1) / 2;++i)
        {
            x = Get_f(edge[i].u), y = Get_f(edge[i].v);
            //加边
            if(x != y)
            {
                ans += edge[i].w;
                fa[x] = y;
                cnt++;
            }
            //加入所有点了
            if(cnt >= n)
                break;
        }
        printf("%d
    ", ans);
    }
    
    int main()
    {
        while(scanf("%d", &n) != EOF && n)
        {
            for(int i = 0;i < n * (n - 1) / 2;++i)
                scanf("%d%d%d", &edge[i].u, &edge[i].v, &edge[i].w);
            Kruskal();
        }
        return 0;
    }

    2、Prim算法

      这算法写起来跟最短路的 Dijkstra算法很像。来复习一下 Dijkstra算法,先找出距离源点最短的点(这里是找出离一个点最近的路),然后把这个点与。源点连接(这里是连接这两个点),之后再更新最短路(更新到各个点的最短的边,这里代码有点不同)。。。就没有哈哈哈哈。。。直接上上面那道模板题代码吧。。。。。

    #include <iostream>
    #include <cstdio>
    #include <string>
    #include <cstring>
    #include <cmath>
    #include <sstream>
    #include <algorithm>
    #include <set>
    #include <map>
    #include <vector>
    #include <queue>
    #include <iomanip>
    #include <stack>
    
    using namespace std;
    
    typedef long long LL;
    const int INF = 0x3f3f3f3f;
    const int MAXN = 1005;
    const int MOD = 1e9 + 7;
    
    #define MemI(x) memset(x, -1, sizeof(x))
    #define Mem0(x) memset(x, 0, sizeof(x))
    #define MemM(x) memset(x, 0x3f, sizeof(x))
    
    int mp[MAXN][MAXN], dis[MAXN], n, ans;
    bool vis[MAXN];
    
    void Prim(int s)
    {
        Mem0(vis);
        int i, j;
        for(i = 1;i <= n;++i)
            dis[i] = mp[s][i];
        dis[s] = 0;
        int v, w;
        vis[s] = true;
        for(i = 1;i < n;++i)
        {
            w = INF;
            for(j = 1;j <= n;++j)
            {
                if(!vis[j] && dis[j] < w)
                {
                    w = dis[j];
                    v = j;
                }
            }
            vis[v] = true;
            ans += w;
            for(j = 1;j <= n;++j)
                if(!vis[j] && dis[j] > mp[v][j])//这里不同
                dis[j] = mp[v][j];
        }
        printf("%d
    ", ans);
    }
    
    int main()
    {
        int a, b, c;
        while(scanf("%d", &n) != EOF && n)
        {
            MemM(mp);
            ans = 0;
            for(int i = 0;i < n * (n - 1) / 2;++i)
            {
                scanf("%d%d%d", &a, &b, &c);
                mp[a][b] = mp[b][a] = c;
            }
            Prim(1);
        }
        return 0;
    }

    3、 STL与kruskal(理论上 Prim算法应该能用优先队列简化找最短路径的操作,就。。不码这个了)

    用map存边,简化排序操作。。。先是模板题练练手,可是跑出来时间慢了十几毫秒

    #include <iostream>
    #include <cstdio>
    #include <string>
    #include <cstring>
    #include <cmath>
    #include <sstream>
    #include <algorithm>
    #include <set>
    #include <map>
    #include <vector>
    #include <queue>
    #include <iomanip>
    #include <stack>
    
    using namespace std;
    
    typedef long long LL;
    const int INF = 0x3f3f3f3f;
    const int MAXN = 1005;
    const int MOD = 1e9 + 7;
    
    #define MemI(x) memset(x, -1, sizeof(x))
    #define Mem0(x) memset(x, 0, sizeof(x))
    #define MemM(x) memset(x, 0x3f, sizeof(x))
    
    int ans, n, fa[MAXN];
    int Get_F(int x)
    {
        if(fa[x] == x)
            return x;
        return fa[x] = Get_F(fa[x]);
    }
    
    int main()
    {
        while(scanf("%d", &n) != EOF  && n)
        {
            ans = 0;
            multimap<int, pair<int, int> > mp;
            for(int i = 1;i <= n;++i)
                fa[i] = i;
            int a, b, c;
            for(int i = 0;i < n * (n  - 1) / 2;++i)
            {
                scanf("%d%d%d", &a, &b, &c);
                mp.insert(make_pair(c, make_pair(a, b)));
            }
            multimap<int, pair<int, int> > :: iterator it;
            int x, y;
            for(it = mp.begin();it != mp.end();++it)
            {
                x = Get_F((*it).second.first), y = Get_F((*it).second.second);
                if(x != y)
                {
                    fa[x] = y;
                    ans += (*it).first;
                }
            }
            printf("%d
    ", ans);
        }
        return 0;
    }

    再给多一道题 UVALive - 6437 ,先用Prim写一下,感觉Prim比较容易理解,然后再用 kruskal写个简单版的。

    Prim思路:。。。。下面注释吧。

    #include <iostream>
    #include <cstdio>
    #include <string>
    #include <cstring>
    #include <cmath>
    #include <sstream>
    #include <algorithm>
    #include <set>
    #include <map>
    #include <vector>
    #include <queue>
    #include <iomanip>
    #include <stack>
    
    using namespace std;
    
    typedef long long LL;
    const int INF = 0x3f3f3f3f;
    const int MAXN = 205;
    const int MOD = 1e9 + 7;
    
    #define MemI(x) memset(x, -1, sizeof(x))
    #define Mem0(x) memset(x, 0, sizeof(x))
    #define MemM(x) memset(x, 0x3f, sizeof(x))
    
    //点数 n,边数 m ,发电站数量 num, 图 mp, 发电站 power
    int ans, n, m, num, vis[MAXN], dis[MAXN], mp[MAXN][MAXN];
    int power[MAXN];
    int Prim()
    {
        int i, j, k;
    //    各个城镇到各个发电站的最短距离
        MemM(dis);
        for(i = 1;i <= n;++i)
            for(j = 0;j < num;++j)
            dis[i] = min(dis[i], mp[i][power[j]]);
        int w, v;
    //    还要对 n - num 个点找最短路
        for(i = 0;i < n - num;++i)
        {
            w = INF;
            for(j = 1;j <= n;++j)
                if(!vis[j] && dis[j] < w)
                {
                    w = dis[j];
                    v = j;
                }
            vis[v] = true;
            ans += w;
    //        这里可以理解为城镇新建了发电站,就看其他城镇到这个新发电站的距离
            for(j = 1;j <= n;++j)
                if(!vis[j] && dis[j] > mp[v][j])
                dis[j] = mp[v][j];
        }
        return ans;
    }
    
    int main()
    {
        int T;
        scanf("%d", &T);
        for(int cas = 1;cas <= T;++cas)
        {
            MemM(mp);
            Mem0(vis);
            ans = 0;
            scanf("%d%d%d", &n, &m, &num);
            for(int i = 1;i <= n;++i)
                mp[i][i] = 0;
            int a, b, c;
            for(int i = 0;i < num;++i)
            {
                scanf("%d", &power[i]);
                vis[power[i]] = true;//这里先处理发电站比较方便
            }
            for(int i = 0;i < m;++i)
            {
                scanf("%d%d%d", &a, &b, &c);
                mp[a][b] = mp[b][a] = c;
            }
            printf("Case #%d: %d
    ", cas, Prim());
        }
        return 0;
    }

    Kruskal。。。这道题把所有发电站都先放到一个集合里,那么最后出来的最小生成树就是答案了。。。。具体点就是把所有发电站的祖先都设为同一个,这样不管连接哪一个发电站都有共同祖先。

    慢了30ms左右

    #include <iostream>
    #include <cstdio>
    #include <string>
    #include <cstring>
    #include <cmath>
    #include <sstream>
    #include <algorithm>
    #include <set>
    #include <map>
    #include <vector>
    #include <queue>
    #include <iomanip>
    #include <stack>
    
    using namespace std;
    
    typedef long long LL;
    const int INF = 0x3f3f3f3f;
    const int MAXN = 205;
    const int MOD = 1e9 + 7;
    
    #define MemI(x) memset(x, -1, sizeof(x))
    #define Mem0(x) memset(x, 0, sizeof(x))
    #define MemM(x) memset(x, 0x3f, sizeof(x))
    
    int ans, n, m, k, fa[MAXN];
    int Get_F(int x)
    {
        if(fa[x] == x)
            return x;
        return fa[x] = Get_F(fa[x]);
    }
    
    int main()
    {
        int T;
        scanf("%d", &T);
        for(int cas = 1;cas <= T;++cas)
        {
            multimap<int, pair<int, int> > mp;
            multimap<int, pair<int, int> > :: iterator it;
            ans = 0;
            scanf("%d%d%d", &n, &m, &k);
            for(int i = 1;i <= n;++i)
                fa[i] = i;
            int a, b, c;
            //处理发电站
            scanf("%d", &a);
            for(int i = 1;i < k;++i)
            {
                scanf("%d", &b);
                fa[b] = a;
            }
            for(int i = 0;i < m;++i)
            {
                scanf("%d%d%d", &a, &b, &c);
                mp.insert(make_pair(c, make_pair(a, b)));
            }
            ans = 0;
            int x, y;
            for(it = mp.begin();it != mp.end();++it)
            {
                x = Get_F((*it).second.first), y = Get_F((*it).second.second);
                if(x != y)
                {
                    ans += (*it).first;
                    fa[x] = y;
                }
            }
            printf("Case #%d: %d
    ", cas, ans);
        }
        return 0;
    }
     
    现在所有的不幸都是以前不努力造成的。。。
  • 相关阅读:
    css3多列
    伪元素
    text文本样式二
    透明登录框
    透明度设置opacity
    超链接
    meta标签
    奇偶选择器
    OC跟Swift混编
    Swift中as as! as?的区别
  • 原文地址:https://www.cnblogs.com/shuizhidao/p/9402165.html
Copyright © 2020-2023  润新知