【每日一题】Kruskal和 Prim算法

发布于:2023-01-04 ⋅ 阅读:(406) ⋅ 点赞:(0)

在这里插入图片描述

Kruskal算法求最小生成树

Kruskal算法求最小生成树

给定一个 n 个点 m 条边的无向图,图中可能存在重边和自环,边权可能为负数。

求最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible。

给定一张边带权的无向图 G=(V,E),其中 V 表示图中点的集合,E 表示图中边的集合,n=|V|,m=|E|。

由 V 中的全部 n 个顶点和 E 中 n−1 条边构成的无向连通子图被称为 G 的一棵生成树,其中边的权值之和最小的生成树被称为无向图 G 的最小生成树。

输入格式
第一行包含两个整数 n 和 m。

接下来 m 行,每行包含三个整数 u,v,w,表示点 u 和点 v 之间存在一条权值为 w 的边。

输出格式
共一行,若存在最小生成树,则输出一个整数,表示最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible。

数据范围
1≤n≤500,
1≤m≤105,
图中涉及边的边权的绝对值均不超过 10000。

输入样例:
4 5
1 2 1
1 3 2
1 4 3
2 3 2
3 4 4
输出样例:
6

题解:

Kruskal是基于全局贪心来完成,他的算法思想比较简单,只需要将边进行按小到大排序,并且保证入的边不会导致形成回路即可。
那么我们可以利用bool st[N]数组进行记录点,记录点是因为假设a,b,c点已经记录,那么接下来入c->d这条边的时候,若c,d在同一个集合当中,说明他们会形成回路。

 #include<iostream>
 using namespace std;
 #include<queue>
 #include<cstring>
 const int N=200010,M=100010;         

 int n,m;
 int p[M];
 struct Edge{
     int src;
     int dst;
     int weight;
     Edge(int _src = -1, int _dst = -1, int _weight = -1)
         :src(_src),
         dst(_dst),
         weight(_weight)
     {}

     bool operator<(const Edge& e)const 
     {
         return this->weight > e.weight;
     }
 }Edges[N];
 int Kruskal()//节点数量
 {
     priority_queue<Edge> pq;
     auto GetRoot = [](int src){
         while(p[src] >= 0)
         {
             src = p[src];
         }
         return src;
     };
     //每一条边入pq
     for(int i = 0;i < N;++i)
     {
         pq.push(Edges[i]);
     }
     //cout << pq.top().weight << endl;
     int res = 0;
     int size = 0;
     while(!pq.empty())
     {
         Edge e = pq.top();
         pq.pop();
         int srci = GetRoot(e.src);
         int dsti = GetRoot(e.dst);
         if(srci != dsti)
         {
             //一边加入另外一边
             p[srci] += p[dsti];
             p[dsti] = srci;
             res += e.weight;
             ++ size;
         }
         else
         {
             continue;
         }
     }
     if(size != n -1)
     return -1;
     else
     return res;
 }
 int main()
 {
     cin >> n >> m;
     //memset(h,-1,sizeof(h));//反码所以最后全是 1111 是吗
     memset(p,-1,sizeof(p));//反码所以最后全是 1111 是吗
     int j = 0;
     while(m --)
     {
         int x,y,z;
         cin >> x >> y >> z;
         Edges[j++] = {x - 1,y - 1 , z};
     }
     int t = Kruskal();
     if(t == -1)
     {
         cout << "impossible" << endl;
     }
     else
     {
         cout << t << endl;
     }
     return 0;
 }

第二种写法,可以不借用优先级队列,直接使用sort进行边的排序即可。

#include<iostream>
using namespace std;
#include<cstring>
#include<cstdio>
const int N=200010,M=100010;         
#include<algorithm>
int n,m;
int p[M];
struct Edge{
    int src;
    int dst;
    int weight;
    Edge(int _src = -1, int _dst = -1, int _weight = -1)
        :src(_src),
        dst(_dst),
        weight(_weight)
    {}
    
    bool operator<(const Edge& e)const 
    {
        return this->weight < e.weight;
    }
}Edges[N];
int Kruskal()//节点数量
{
    auto GetRoot = [](int src){
        while(p[src] >= 0)
        {
            src = p[src];
        }
        return src;
    };
    //每一条边入pq
    sort(Edges,Edges + m);
    //cout << Edges[0].weight << endl;
    int res = 0;
    int size = 0;
    for(int i = 0;i < m;++i)
    {
        int srci = GetRoot(Edges[i].src);
        int dest = GetRoot(Edges[i].dst);
        if(srci != dest)
        {
            p[srci] += p[dest];
            p[dest] = srci;
            res += Edges[i].weight;
            ++ size;
        }
    }
    if(size != n -1)
    return -1;
    else
    return res;
}
int main()
{
    cin >> n >> m;
    ios::sync_with_stdio(false);
    //memset(h,-1,sizeof(h));//反码所以最后全是 1111 是吗
    memset(p,-1,sizeof(p));//反码所以最后全是 1111 是吗

    for(int i = 0;i < m;++i)
    {
        int x,y,z;
        scanf("%d%d%d", &x, &y, &z);
        Edges[i] = {x - 1,y - 1 , z};
    }
    int t = Kruskal();
    if(t == -1)
    {
        cout << "impossible" << endl;
    }
    else
    {
        cout << t << endl;
    }
    return 0;
}

Prim算法求最小生成树

Prim算法求最小生成树

给定一个 n 个点 m 条边的无向图,图中可能存在重边和自环,边权可能为负数。

求最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible。

给定一张边带权的无向图 G=(V,E),其中 V 表示图中点的集合,E 表示图中边的集合,n=|V|,m=|E|。

由 V 中的全部 n 个顶点和 E 中 n−1 条边构成的无向连通子图被称为 G 的一棵生成树,其中边的权值之和最小的生成树被称为无向图 G 的最小生成树。

输入格式
第一行包含两个整数 n 和 m。

接下来 m 行,每行包含三个整数 u,v,w,表示点 u 和点 v 之间存在一条权值为 w 的边。

输出格式
共一行,若存在最小生成树,则输出一个整数,表示最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible。

数据范围
1≤n≤105,
1≤m≤2∗105,
图中涉及边的边权的绝对值均不超过 1000。

输入样例:
4 5
1 2 1
1 3 2
1 4 3
2 3 2
3 4 4
输出样例:
6

题解:

dist在这里是表示到达集合的距离。

  • Prim基于局部贪心,由给定的一点出发,每次寻找离集合最近的一条边,dist记录的是该点到集合的距离。这个点要十分注意,dist[j] = min(dist[j],g[t][j]);更新的代码也就有所不同,g[t][j]就是j这个点到集合的距离。
  • 每次更新出一个点,更新这个点到其他点的距离,更新n轮,共n个点。
#include<iostream>
using namespace std;
#include<cstring>
const int N = 510;
int g[N][N];
int dist[N];
bool st[N];

int n,m;
int Prim()
{
    dist[0] = 0;
    int size = 0;
    int res= 0;
    for(; size < n; ++ size)
    {
        int t = -1;
        //找最小的点进行更新
        for(int j = 0;j < n; ++ j)
        {
            if(!st[j] && (t == -1 || dist[j] < dist[t]))//如果没有在树中,且到树的距离最短,则选择该点
                t = j;
        }
        // for(int j = 0;j < n;++j)
        // {
        //     cout << dist[j] << " ";
        // }
        //cout << "================" << endl;
        //cout << t << " " << st[t] << " " << dist[t]<< endl;
        if(dist[t] == 0x3f3f3f3f) return 0x3f3f3f3f;
        st[t] = true;
        
        res += dist[t];
        //cout<< t + 1 << " " << res << endl;
        for(int j = 0;j < n;++j)
        {
            if(g[t][j] != 0x3f3f3f3f && !st[j])//这个点不能是加入st数组当中的。
            {
                
                dist[j] = min(dist[j],g[t][j]);
                //cout << t+1 <<  "->" << j+1 << ":" << dist[j] << " ";
            }
        }
        //cout << endl;
    }
    if(size == n)
    return res;
    else
    return 0x3f3f3f3f;
}
int main()
{
    cin >> n >> m;
    memset(dist,0x3f,sizeof(dist));
    memset(g,0x3f,sizeof(g));
    while(m --)
    {
        int x,y,z;
        cin >> x>> y >> z;
        if(x == y) continue;
        g[x - 1][y - 1] = min(z,g[x-1][y - 1]);//重边
        g[y - 1][x - 1] = g[x - 1][y - 1];//重边
    }
    int res = Prim();
   
    if(res == 0x3f3f3f3f) cout << "impossible";
    else cout << res << endl;
    return 0;
}



end

  • 喜欢就收藏
  • 认同就点赞
  • 支持就关注
  • 疑问就评论