代码随想录打卡第五十七天

代码随想录–图论部分

day 55 图论第七天



一、卡码网53–寻宝

代码随想录题目链接:代码随想录

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

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

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

这个与生成树有关

什么是生成树?最小生成树是所有节点的最小连通子图, 即:以最小的成本(边的权值)将图中所有节点链接到一起

如果有n个节点,那么肯定可以用n-1个边连起来,变成一个树

怎么找这个边呢?有两种算法,一是prim算法,二是kruskal算法

先说prim算法,核心思路是贪心算法

第一步,选距离生成树最近节点
第二步,最近节点加入生成树
第三步,更新非生成树节点到生成树的距离(即更新minDist数组)

这里minDist数组 用来记录 每一个节点距离最小生成树的最近距离

那么初始化该数组就是全部设为最大值

在这里插入图片描述

为了方便理解,数组构建成8维的,第一位0不对应任何,这样下标和节点号一一对应

随便取一个点进行遍历,取1,加入生成树节点中,那么minDist变为:
在这里插入图片描述
第二次迭代:找非生成树节点中距离生成树节点最近的节点(2或者3都行,都是最短的),加入生成树节点中

再更新minDist数据如下:

在这里插入图片描述
按照这个顺序类推下去,得到最后的结果:
在这里插入图片描述
对于本题,要求权重之和,那也就是把minDist求和即可

代码如下:

#include <iostream>
#include <vector>
#include <climits>

using namespace std;
int main()
{
    int v, e;
    int x, y, k;
    cin >> v >> e;
    vector<vector<int>> grid(v + 1, vector<int>(v + 1, 10001));
    while(e --)
    {
        cin >> x >> y >> k;
        grid[x][y] = k;
        grid[y][x] = k;
    }
    
    vector<int> minDist(v + 1, 10001);
    vector<bool> isInTree(v + 1, false);
    
    for(int i = 1; i < v; i ++)
    {
        int cur = -1;
        int minVal = INT_MAX;
        for(int j = 1; j <= v; j ++)
        {
            if(!isInTree[j] && minDist[j] < minVal)
            {
                minVal = minDist[j];
                cur = j;
            }
        }
        
        isInTree[cur] = true;
        for(int j = 1; j <=v; j ++)
        {
            if(!isInTree[j] && grid[cur][j] < minDist[j]) minDist[j] = grid[cur][j];
        }
    }
    
    int result = 0;
    for(int i = 2; i <= v; i ++)
    {
        result += minDist[i];
    }
    cout << result << endl;
}

再说kruskal算法。prim算法中是把节点一个一个加入生成树中,所以可以说是维护节点的方法

kruskal算法则是维护边的方法

边的权值排序,因为要优先选最小的边加入到生成树里
遍历排序后的边:

如果边首尾的两个节点在同一个集合,说明如果连上这条边图中会出现环
如果边首尾的两个节点不在同一个集合,加入到最小生成树,并把两个节点加入同一个集合

以本题为例的话,把权值排列一下,先加入最短的边
在这里插入图片描述
通过判断节点是否在一个集合内,来决定要不要加入该条边
在这里插入图片描述
遍历到最后就和prim算法的结果一样

难点在于怎么判断是不是在一个集合内?使用并查集

代码如下:

#include <iostream>
#include <vector>
#include <climits>
#include <algorithm>

using namespace std;

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

// 并查集模板
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;
}

int main()
{
    int v, e;
    int v1, v2, val;
    vector<Edge> edges;
    int result_val = 0;
    cin >> v >> e;
    while (e--) 
    {
        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();
    
    for(Edge edge : edges)
    {
        int x = find(edge.l);
        int y = find(edge.r);
        
        if(x != y)
        {
            result_val += edge.val;
            join(x, y);
        }
    }
    cout << result_val << endl;
    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值