代码随想录-训练营-day48

53. 寻宝(第七期模拟笔试) (kamacoder.com)

这样的无向有权图,求联通整个图的最小路径总距离,我们可以统称为最小生成树问题,一般来说我们有两种做法:prim算法和krustal算法,一个去维护点的集合,一个则是去维护边的集合。

我们先从Prim算法开始:

具体的推算过程我就不赘述了,总的来说是贪心思想的一种体现。

代码如下:

#include<iostream>
#include<vector>
#include<climits>
using namespace std;
int main(){
    int v,e;
    cin>>v>>e;
    int v1,v2,val;
    vector<vector<int>> grid(v+1,vector<int>(v+1,10001));
    vector<int> minDistance(v+1,10001);
    vector<bool> isInTree(v+1,false);
    for(int i=0;i<e;++i){
        cin>>v1>>v2>>val;
        grid[v1][v2]=val;
        grid[v2][v1]=val;
    }
    for(int i=1;i<v;++i){
        int cur=-1,min_val=INT_MAX;
        for(int j=1;j<=v;++j){
            if(!isInTree[j]&&min_val>minDistance[j]){
                min_val=minDistance[j];
                cur=j;
            }
        }
        isInTree[cur]=true;
        for(int j=1;j<=v;++j){
            if(!isInTree[j]&&minDistance[j]>grid[cur][j]){
                minDistance[j]=grid[cur][j];
            }
        }
    }
    int res=0;
    for(int i=2;i<=v;++i){
        res+=minDistance[i];
    }
    cout<<res<<endl;
    return 0;
}

我们先随便把一个节点丢进我们的最小生成树(之所以可以随便丢是因为最后我们所有的节点都会在最小生成树里,只是早晚的区别),按照习惯的话我们就把第一个节点丢进去即可,然后我们检查当前节点的最小距离是否小于维护的最小值,如果小于的话就更新维护的最小值并将当前节点丢进最小生成树,最小生成树每多一个节点就去更新我们当前节点的最小距离即可。

最后遍历相加权值时之所以从二开始是因为我们v个节点应该只有v-1条边,我们只记录非起点的节点的边权重即可。

然后是我们的k算法:

代码如下:

#include<iostream>
#include<vector>
#include <algorithm>
using namespace std;
int n=10001;
vector<int> father(n,-1);
void init(){
    for(int i=0;i<n;++i){
        father[i]=i;
    }
}
int find(int u){
    return u==father[u]?u:father[u]=find(father[u]);
}
void join(int u,int v){
    u=find(u);
    v=find(v);
    if(u==v)return;
    father[v]=u;
}
struct Edge{
    int l,r,val;
};
int main(){
    int v,e;
    int v1,v2,val;
    cin>>v>>e;
    vector<Edge> edges;
    for(int i=0;i<e;++i){
        cin>>v1>>v2>>val;
        edges.push_back({v1,v2,val});
    }
    sort(edges.begin(),edges.end(),[](const Edge& a,const Edge& b){
        return a.val<b.val;
    });
    init();
    int res=0;
    for(Edge edge:edges){
        int x=find(edge.l);
        int y=find(edge.r);
        if(x!=y){
            res+=edge.val;
            join(x,y);
        }
    }
    cout<<res<<endl;
    return 0;
}

核心思路就是去对边的权重进行排序后通过并查集按顺序检测两个点是否以及在同一个集合里,如果不在的话就进行联合并添加权重。