SDUT2144 最小生成树

版权声明:文章全是博主转载, 请随意操作。 https://blog.csdn.net/ggqinglfu/article/details/82012295

最小生成树-Prim算法和Kruskal算法
概念一 并查集:(union-find sets)

baby 一定要记得 哟 ——它是一种简单的用途广泛的集合. 并查集是若干个不相交集合,能够实现较快的合并判断元素所在集合的操作,应用很多,如其求无向图的连通分量个数等。最完美的应用当属:实现Kruskar算法求最小生成树
由于感觉原作者博文写的挺好,所以直接粘上博主链接1
并查集的优化

1、Find_Set(x)时 路径压缩
寻找祖先时我们一般采用递归查找,但是当元素很多亦或是整棵树变为一条链时,每次Find_Set(x)都是O(n)的复杂度,有没有办法减小这个复杂度呢?
答案是肯定的,这就是路径压缩,即当我们经过”递推”找到祖先节点后,“回溯”的时候顺便将它的子孙节点都直接指向祖先,这样以后再次Find_Set(x)时复杂度就变成O(1)了,如下图所示;可见,路径压缩方便了以后的查找。
看图请点击链接1

2、Union(x,y)时 按秩合并
即合并的时候将元素少的集合合并到元素多的集合中,这样合并之后树的高度会相对较小。
老板, 来一碗 “常见的简易Merge(函数)水煮肉片!!!”

int fin(int x) // 寻根 (可以递归,可以循环)
{
    if(f[x]==x)
        return x;
    else
    {
        f[x]=fin(f[x]);
        return f[x];
    }
}
int Merge(int u,int v)
{
    int t1,t2;
    t1=fin(u);
    t2=fin(v);
    if(t1!=t2)
    {
        f[t2]=t1;///左边原则; 我就是看到这个词才粘的它
        return 1;
    }
    else
    return 0;
}

再来一碟 详细图解版的灌汤包
最小生成树-Prim算法和Kruskal算法链接2
图解过程很清晰。

part.2 题与代码
山东理工 ACM 链接
数据结构实验之图论九:最小生成树链接0
Time Limit: 1000 ms Memory Limit: 65536 KiB

Problem Description
有n个城市,其中有些城市之间可以修建公路,修建不同的公路费用是不同的。现在我们想知道,最少花多少钱修公路可以将所有的城市连在一起,使在任意一城市出发,可以到达其他任意的城市。

Input
输入包含多组数据,格式如下。

第一行包括两个整数n m,代表城市个数和可以修建的公路个数。(n <= 100, m <=10000)

剩下m行每行3个正整数a b c,代表城市a 和城市b之间可以修建一条公路,代价为c。

Output
每组输出占一行,仅输出最小花费。

Sample Input
3 2
1 2 1
1 3 1
1 0
Sample Output
2
0
Hint
Source
赵利强

思路:
如果想要让一个有n个点的图是连通图,那至少需要n-1条边,一个连通无向图且不含有回路那么他就是一个树,那么一个有n个点的图可以找到n-1条边保持它的连通性并且不含有回路,那么就找到了这个图的最小生成树(概念)
think:
找n-1条边,边的权值越小越好,那就进行升序排序,接下来就是判断当前边的两个顶点是否已经连通,以免有产生回路的情况。(该思路原作者博客链接 诚心学习, 顺便分享, 不是推广)

以下模块:
我所关注的学姐 写的代码讲解 她 的 博客
图结构练习——最小生成树(Prim+Kruskal)链接3
接下来就是关羽面前耍大刀喽

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <string.h>
#include <cstdlib>
#define INF 0x3f3f3f3f
#define MAXN 11111
using namespace std;
//Kruskal:以边来计算的,每次寻找最小的边,满足则加费用
struct node
{
    int u, v, w;
}s[MAXN];
int pre[MAXN]; //存祖先

bool cmp(struct node x, struct node y)
{
    return x.w < y.w;
}

int root(int a)
{
    int r = a;
    while(r != pre[r]) //寻找祖先节点
    {
        r = pre[r];
    }
    int b = a, c;
// 为了实现 路径压缩(可能元素不是很多, 没必要压缩吧)
    while(b != r)
    {
        c = pre[b];
        pre[b] = r;
        b = c;
    }
    return r;
}

void Kruskal(int n, int m)
{
    int sum = 0, ans = 0; // 统计输出完一次归零继下一次
    for(int i = 0; i <= n; i++)
    {
        pre[i] = i; //初始化
    }//初始化后每一个元素的父亲节点是它本身,
    //每一个元素的祖先节点也是它本身(也可以根据情况而变)
    for(int i = 0; i < m; i++) //遍历每一条边
    {
        int x = root(s[i].u);
        int y = root(s[i].v);
 if(x != y)//判断两个元素是否属于同一集合,只要看其所在集合的祖先是否相同即可
        {
            ans += s[i].w;
            pre[x] = y;
            sum += 1;
            if(sum == n - 1)
                break;
        }
    }
    printf("%d\n", ans);
}
int main()
{
    int n, m;
    while(~scanf("%d %d",&n, &m))
    {
        for(int i = 0; i < m; i++)
        {
            scanf("%d %d %d", &s[i].u, &s[i].v, &s[i].w);
        }
        sort(s, s + m, cmp); // 先排序,sort 比qsort 里面写的少点
        Kruskal(n, m);
    }
    return 0;
}

注释多了很难看。。。。。。。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <string.h>
#include <cstdlib>
#define MAXN 11111
using namespace std;           //Kruskal:以边来计算的,每次寻找最小的边,满足则加费用
struct node
{
    int u, v, w;
}s[MAXN];
int pre[MAXN];                 //存祖先
bool cmp(struct node x, struct node y)
{
    return x.w < y.w;
}
int root(int a)
{
    int r = a;
    while(r != pre[r])          //寻找祖先节点
    {
        r = pre[r];
    }
    int b = a, c;               // 为了实现 路径压缩(可能元素不是很多, 没必要压缩吧)
    while(b != r)
    {
        c = pre[b];
        pre[b] = r;
        b = c;
    }
    return r;
}
void Kruskal(int n, int m)
{
    int sum = 0, ans = 0;       // 统计输出完一次归零继下一次
    for(int i = 0; i <= n; i++)
    {
        pre[i] = i;             //初始化
    }                           //初始化后每一个元素的父亲节点是它本身,每一个元素的祖先节点也是它本身(也可以根据情况而变)
    for(int i = 0; i < m; i++)  //遍历每一条边
    {
        int x = root(s[i].u);
        int y = root(s[i].v);
           if(x != y)           //判断两个元素是否属于同一集合,只要看其所在集合的祖先是否相同即可
        {
            ans += s[i].w;
            pre[x] = y;
            sum += 1;
            if(sum == n - 1)
                break;
        }
    }
    printf("%d\n", ans);
}
int main()
{
    int n, m;
    while(~scanf("%d %d",&n, &m))
    {
        for(int i = 0; i < m; i++)
        {
            scanf("%d %d %d", &s[i].u, &s[i].v, &s[i].w);
        }
        sort(s, s + m, cmp); // 先排序,sort 比qsort 里面写的少点
        Kruskal(n, m);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/ggqinglfu/article/details/82012295
今日推荐