自学内容网 自学内容网

代码随想录算法训练营第57天 | 寻宝

 寻宝

题目描述

在世界的某个区域,有一些分散的神秘岛屿,每个岛屿上都有一种珍稀的资源或者宝藏。国王打算在这些岛屿上建公路,方便运输。

不同岛屿之间,路途距离不同,国王希望你可以规划建公路的方案,如何可以以最短的总公路距离将 所有岛屿联通起来(注意:这是一个无向图)。 

给定一张地图,其中包括了所有的岛屿,以及它们之间的距离。以最小化公路建设长度,确保可以链接到所有岛屿。

输入描述

第一行包含两个整数V 和 E,V代表顶点数,E代表边数 。顶点编号是从1到V。例如:V=2,一个有两个顶点,分别是1和2。

接下来共有 E 行,每行三个整数 v1,v2 和 val,v1 和 v2 为边的起点和终点,val代表边的权值。

输出描述

输出联通所有岛屿的最小路径总距离

输入示例

7 11
1 2 1
1 3 1
1 5 2
2 6 1
2 4 2
2 3 2
3 4 1
4 5 1
5 6 2
5 7 1
6 7 1

输出示例

6

提示信息

数据范围:
2 <= V <= 10000;
1 <= E <= 100000;
0 <= val <= 10000;

如下图,可见将所有的顶点都访问一遍,总距离最低是6.

思路:这是生成最小生成树的经典题目,涉及到了prim算法以及kruskal算法。

1. prim算法 

prim算法从结点出发构造最小生成树,适合于稠密图(边多的图)。

主要分为三步:

第一步选择最接近最小生成树的结点;

第二步标记该结点并将其加入最小生成树中;

第三步更新最小生成树的路径大小。

#include<iostream>
#include<vector>
#include<climits>
using namespace std;

int main(){
    int v, e;
    int v1, v2, val;
    cin >> v >> e;
    
    vector<vector<int>> grid(v + 1, vector<int>(v + 1, 10001));
    
    for(int i = 0; i < e; i ++){
        cin >> v1 >> v2 >> val;
        grid[v1][v2] = val;
        grid[v2][v1] = val;
    }
    
    
    vector<int> minDist(v + 1, 10001);
    
    vector<bool> visited(v + 1, false);
    
    for(int i = 1; i < v; i ++){
        int cur = -1;//第一步选择最接近最小生成树的结点
        int min = INT_MAX;
        for(int j = 1; j <= v; j ++){
            if(!visited[j] && minDist[j] < min){
                min = minDist[j];
                cur = j;
            }
        }
        visited[cur] = true;//第二步开始进行标记
        for(int k = 0; k <= v; k ++){//第三步开始更新路径
            if(!visited[k] && grid[cur][k] < minDist[k]){
                minDist[k] = grid[cur][k];
            }
        }
    }
    
    int result = 0;
    for(int i = 2; i <= v; i ++){
        result += minDist[i];
    }
    cout << result << endl;
}

2. kruskal算法

kruskal算法是从边出发生成最小生成树,适用于稀疏图(边少的图)。

这里需要首先对边的权值进行排序,所以涉及到将所有边统计下来,然后后面不断加入并查集,判断是否处于同一个集合中。

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

struct edge{
  int l, r, val;  
};

int v, e;
vector<int> parent(10001, 0);

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

int find(int u){
    return u == parent[u] ? u : parent[u] = find(parent[u]);
}

bool isSame(int u, int v){
    u = find(u);
    v = find(v);
    return u == v;
}

void join(int u, int v){
    u = find(u);
    v = find(v);
    if(u == v) return;
    parent[v] = u;
}

bool cmp(edge& a, edge& b){
    return a.val < b.val;
}
int main(){
    while(cin >> v >> e){
        init();
        vector<edge> grid(e);
        int result = 0;//统计所有的权值
        for(int i = 0; i < e; i ++){
            cin >> grid[i].l >> grid[i].r >> grid[i].val;
        }
        
        //对边的权值按照由小到大的顺序排列
        sort(grid.begin(), grid.end(), cmp);
        
        for(int i = 0; i < e; i ++){
            if(isSame(grid[i].l, grid[i].r)) continue;
            result += grid[i].val;
            join(grid[i].l, grid[i].r);
        }
        cout << result << endl;
    }
}

感谢你的阅读,希望我的文章能够给你帮助,如果有帮助,麻烦点赞加收藏,或者点点关注,非常感谢。

如果有什么问题欢迎评论区讨论! 

 


原文地址:https://blog.csdn.net/m0_68237332/article/details/142580754

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!