前言

两者思路都为贪心。
Kruskal是通过对所有边排序,每次找不会成环的权值最小边。
Prim是找到未更新的最近节点,用该节点对其他未更新节点进行松弛,标记该节点已更新,再重复上述直至全部节点更新。

稀疏图:Kruskal 优
稠密图:Prim 优。

当然也可在阿鑫的半成品个人博客网站上阅读:阿鑫的学习小站


洛谷P3366 【模板】最小生成树

题目描述
如题,给出一个无向图,求出最小生成树,如果该图不连通,则输出 orz。
输入格式
第一行包含两个整数 N,M,表示该图共有 N 个结点和 M 条无向边。
接下来 M 行每行包含三个整数 X i ,Y i,Z i,表示有一条长度为 Z i 的无向边连接结点 X i ,Y i。
输出格式
如果该图连通,则输出一个整数表示最小生成树的各边的长度之和。如果该图不连通则输出 orz。
输入输出样例
输入
4 5
1 2 2
1 3 2
1 4 3
2 3 4
3 4 3
输出
7

Kruskal

时间复杂度:O(MlogM)
思路: 对所有边依据权值从小到大排序 , 按顺序找出 n-1条 可用边(即不会成环的边)

思路 一> code:
1.对所有边依据权值从小到大排序
一> 用优先队列存储边,对于边可以构造一个结构体,记录 起点、终点、权值
2.队列空前找出 n-1条 可用边(即不会成环的边)
一> 从队首不断读取和弹出头节点(当前最短边),利用 并查集 判断是否成环(如果已经在集合内,则会成环,舍去),如果不成环则取用该边,将其加入生成树集合。如果队空前已找到了 n-1条 边可提前结束

ac题解

//用时:258ms(O2优化:138ms)            内存:4.36MB
#include <bits/stdc++.h>
using namespace std;
struct node{
    int x;
    int y;
    int z;    
    //对结构体的 > 重载  ,否则 greater<node>用不了 
     bool operator > (const node& _T)const
    {
        return z > _T.z;
    }
};
priority_queue<node,vector<node>,greater<node> > q;

/* 上述还可写成下面这种形式 
struct node{
    int x;    
    int y;
    int z;    
};
struct cmp
{
    template <typename T>
    bool operator()(T const &a, T const &b)
    {
        return a.z>b.z;
    }
};
priority_queue<node,vector<node>,cmp> q;
*/ 

//并查集模板 
long long ans;
int par[5005];
void init(int n,int par[]){
    for(int i=1;i<=n;i++) 
        par[i]=i;
}
int find(int x){
    if(par[x]==x){
        return x;
    }else{
        par[x]=find(par[x]);
        return par[x];
    }
}
int merge(int x,int y){
    int tx,ty;
    tx=find(x);
    ty=find(y);
    if(tx==ty){    //如果在一个集合,返回0 
        return 0;
    }else{        //不在一个集合,合并两集合,返回1 
        par[ty]=tx;
        return 1;
    }
}

//主函数  
int main()
{
    int i,j,n,m;
    int flag;    //记录 取到边 的数目 
    cin>>n>>m;
    for(i=1;i<=m;i++){
        node t;
        scanf("%d%d%d",&t.x,&t.y,&t.z);//按题意输入 
        q.push(t);    //压入优先队列完成排序 
    }
    init(n,par); //初始化集合 

    while(!q.empty()&&flag<n-1){    //队不为空(还有边没判断),且没找到n-1条边 
        node t=q.top();            //取出当前最短边 
        if(merge(t.x,t.y)){        //不在同一集合则该边可取 
            flag++;
            ans+=t.z;
        }
        q.pop();                //弹出该边 
    }

    if(flag==n-1) cout<<ans<<endl;    //找到了n-1条边,输出答案 
    else cout<<"orz"<<endl;        //否则无法生成最小生成树 
    return 0;
}

Prim

时间复杂度:
思路:

思路 一> code:
ac题解

#include <bits/stdc++.h>
using namespace std;

priority_queue<pair<int,int>, vector<pair<int, int> >,greater<pair<int,int> > > q;
//greater<pair<int,int>会先比较.first如果相等再比较.second  
vector<pair<int,int> > a[5005];

int dis[5005];
long long ans;
int book[5005];

int main()
{
    memset(dis,0x3f,sizeof(dis)); //初始化默认距离都为 很大
    int i,j,n,m;
    int x,y,z;
    cin>>n>>m;
    for(i=1;i<=m;i++){
        scanf("%d%d%d",&x,&y,&z);    //输入并用邻接表存储 
        a[x].push_back(make_pair(y,z));
        a[y].push_back(make_pair(x,z));
    }
    int t=1;    //将 结点1 作为 起点 
    dis[t]=0;
    book[t]=1;
    int mn = 999999999;
    for(i=1;i<n;i++){
        for(j=0;j<a[t].size();j++){        //遍历与 t 相邻的结点 
            if(book[a[t][j].first]==0){    //如果未被访问 
                dis[a[t][j].first] = min(dis[a[t][j].first],dis[t]+a[t][j].second);//松弛 
                q.push(make_pair(a[t][j].second,a[t][j].first)); //将该点压入优先队列 
            }
        }
        if(q.empty()) break;        
        while(!q.empty()){    //找到未选过的最近点 
            t=q.top().second;
            if(book[t]==1) q.pop();
            else break;
        }

        if(book[t]==1) break;
        ans+=q.top().first;
        q.pop();
        book[t]=1;
    }

    if(i==n) cout<<ans<<endl;
    else cout<<"orz"<<endl;
    return 0;
}
Copyright © 阿鑫 2022 all right reserved,powered by Gitbook最初发布时间: 2022-02-13

results matching ""

    No results matching ""