洛谷-P3366 最小生成树

题目链接:洛谷-P3366

## 题目描述: 如题,给出一个无向图,求出最小生成树,如果该图不连通,则输出orz

输入输出格式
输入格式:
第一行包含两个整数N、M,表示该图共有N个结点和M条无向边。(N<=5000,M<=200000)

接下来M行每行包含三个整数Xi、Yi、Zi,表示有一条长度为Zi的无向边连接结点Xi、Yi

输出格式:
输出包含一个数,即最小生成树的各边的长度之和;如果该图不连通则输出orz

思路

Prim算法:
Prim算法就是定义mat用来存图,dist数组用来存每个点到最小生成树的最短距离,vis数组标记此点是否已经在最小生成树中了(防止出现环)。
实际操作就是先随便把一个点加入到生成树里(一般是第一个点),然后寻找与该点距离最短的点并加入,然后在寻找与1,2点相连最短的点,并加入到生成树中,就这样不断的加下去,直到加入的点数与原图一致终止;
可以自己手动模仿一下过程,其实还是好理解的!
Kruskal算法:
对边进行排序,依次加边,加边时需要判断是否在同一棵树,在同一颗树加边会产生环,如何判断是否在同一棵树?这里我们用到并查集,当当前边的左右端点不在同一棵树则可以进行合并,当加入的点数和给出的顶点数一致时就跳出循环,范围最小生成树的值,如果遍历完后还是不够给定的定点数,则其无法构成生成树!

易错点

1.每次加入前确保加入的权值比之前加入的权值要小,否则不加入;
如 第一次 给出 1-2边的权值为3, 然后某次加边操作说 1-2边权值为10, 这时候我们保留第一次的权值(保留权值较小的那次)
2.一般Prim适用于稠密图,用邻接矩阵存顶点;
3.Prim算法是存储顶点,Kruskal算法是存储边,注意给出的数据范围进行存储;

代码:

Prim算法

#include 
using namespace std;

#define INF 0x3f3f3f3f

int n, m, dist[5010], mat[5010][5010];
bool vis[5010];

void init(int k)
{
    for(int i = 1; i<=k; i++)
    {
        for(int j = 1; j<=k; j++)
        {
            mat[i][j] = INF;
        }
        dist[i] = INF;
    }
}

void Union(int x, int y, int val)
{
    if(val < mat[x][y])
    {
        mat[x][y] = mat[y][x] = val;
    }
}

int Prim()
{
    int sum = 0;
    dist[1] = 0;
    for(int i = 1; i<=n; i++)
    {
        int min_dist = INF, min_vertex;
        for(int j = 1; j <= n; j++)
        {
            if(!vis[j] && min_dist > dist[j])
            {
                min_dist = dist[j];
                min_vertex = j;
            }
        }
        vis[min_vertex] = 1;
        sum += min_dist;
        for(int j = 1; j <= n; j++)
        {
            if(!vis[j] && mat[min_vertex][j] < dist[j])
            {
                dist[j] = mat[min_vertex][j];
            }
        }
    }
    return sum;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> m;
    init(n);
    memset(vis, 0, sizeof(vis));
    for(int i = 0; i<m; i++)
    {
        int x, y, val;
        cin >> x >> y >> val;
        Union(x, y, val);
    }
    cout << Prim() << endl;
    return 0;
}




Kruskal算法

#include 
using namespace std;

#define MAX 200100

struct Node{
    int x, y, val;
}S[MAX];

int pre[MAX];

bool cmp(Node a, Node b)
{
    return a.val < b.val;
}

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

int Find(int x)
{
    int r = x;
    while(r != pre[r])
    {
        r = pre[r];
    }
    int i = x, j;
    while(i != r)
    {
        j =pre[i];
        pre[i] = r;
        i = j;
    }
    return r;
}

int Kruskal(int n, int m)
{
    int sum = 0, ans = 0;
    for(int i = 0; i<m; i++)
    {
        int x = Find(S[i].x);
        int y = Find(S[i].y);
        if(x != y)
        {
            sum += S[i].val;
            pre[x] = y;
            ans++;
        }
        if(ans == n)
            return sum;
    }
    return sum;
}

int main()
{
    int n, m;
    cin >> n >> m;
    init(n);
    for(int i = 0; i<m; i++)
    {
        cin >> S[i].x >> S[i].y >> S[i].val;
    }
    sort(S, S+m, cmp);
    cout << Kruskal(n, m) << endl;
    return 0;
}

你可能感兴趣的:(洛谷)