leetcode 1135. 最低成本联通所有城市

题目描述

想象一下你是个城市基建规划者,地图上有 N 座城市,它们按以 1 到 N 的次序编号。

给你一些可连接的选项 conections,其中每个选项 conections[i] = [city1, city2, cost] 表示将城市 city1 和城市 city2 连接所要的成本。(连接是双向的,也就是说城市 city1 和城市 city2 相连也同样意味着城市 city2 和城市 city1 相连)。

返回使得每对城市间都存在将它们连接在一起的连通路径(可能长度为 1 的)最小成本。该最小成本应该是所用全部连接代价的综合。如果根据已知条件无法完成该项任务,则请你返回 -1。

示例 1:

输入:N = 3, conections = [[1,2,5],[1,3,6],[2,3,1]]
输出:6
解释:
选出任意 2 条边都可以连接所有城市,我们从中选取成本最小的 2 条。
示例 2:

输入:N = 4, conections = [[1,2,3],[3,4,4]]
输出:-1
解释:
即使连通所有的边,也无法连接所有城市。
提示:
1 <= N <= 10000
1 <= conections.length <= 10000
1 <= conections[i][0], conections[i][1] <= N
0 <= conections[i][2] <= 10^5
conections[i][0] != conections[i][1]

题解

这题乍一看,就知道是最小生成树问题了,正好,借此机会,复习一把最小生成树。

解法1:kruskal算法

kruskal算法核心思想:
1)初始阶段,每个点互不相识,各自为一个孤岛。
2)以题设给定的“边”为入手,不断的通过整合边所连接两个点,让所有孤岛都连接到一起。
3)利用贪心算法,选择cost小的边为起点,遍历所有的边。
4)遍历的过程中,如果发现当前边所在的两个点在两个孤岛上,则将他们合并。这一步采用的并查集方法(即为不同的集合寻找father,father相同的节点,为同一个集合)。
思想还是比较清晰和简洁,我没有去证明为什么第三步用贪心算法一定是可以的。。有兴趣的可以查阅资料再证明一下。

代码

class Solution {
public:
    int find(int x) //不断向上找,直到找到下标和元素相同的点;不理解背后原因的,可以学习下树的数组表示发。
    {
        while(x != p[x]) x = p[x];
        return x;
    }
    int minimumCost(int N, vector<vector<int>>& connections) {
        int ans = 0;
        int point_num = 0;
        //sort重定义比较是重载的<号,为真则不交换,否则交换。
       //基于上述原则,对cmp返回对应的true or false
        auto cmp = [](vector<int> &a,  vector<int> &b){return a[2] < b[2];};
        for(int i = 0; i <= N; i++) {
            p.push_back(i);
        }
        sort(connections.begin(), connections.end(), cmp);//按cost值排序,采用贪心算法
        for (auto conn : connections) {
            int px = find(conn[0]),py = find(conn[1]);
            if(px != py) { //如果该边所在的两个节点不在同一个集合
                p[px] = py; //合并集合
                ans += conn[2];
                point_num++;
                //对于无向图的话,至少需要n-1条边可以使得图是联通的;
                //如果对于有向图的话,至少需要n条边才可以使得图是联通的
                if(point_num == N - 1){  
                    return ans;
                } 
            } 
        }
        return -1;
    }
private:
    vector<int> p; //集合关系表,用一个数组来描述N个节点的集合关系;等同于树的数组表示方法。
};

kruskal就是贪心加并查集,,而且写起来也比较简单,非常的模板化。

解法2:prim算法

prim算法核心思想
1)以某一个点为起点(通常为数组的第一个点)。以“点”为参照物,从该点所在“集合”的边当中依次选择cost最小的边去连接一个“新的”节点。
2)在一个结合新(要注意是新的,否则会死循环)加入一个节点后,将会扩大原有的集合,而该集合所连接的边和点都会发生变化。针对这类问题,选择小顶堆作为数据结构。
3)当该集合中有N个点时,所有的点都已经被访问过了。

代码

class Solution {
public:
    int minimumCost(int N, vector<vector<int>>& connections) {
        //重载priority_queue的比较函数,priority_queue默认是大顶堆,重载的是<号
        //默认情况下如果左边参数大于右边参数,则说明左边形参的优先级低于右边形参,会将左边的放到后面
        //构建小顶堆时,我们实现一个>号的判断即可,大于返回true,优先级低,被放到后面,则小的会放前面
        struct cmp {
            bool operator () (const vector<int> &a, const vector<int> &b) {
                return a[2] > b[2];
            }
        };
        int selected = 0, ans = 0;
        //构建点和cost的集合关系,本质是个三维数组,第一维是起点,二维是<终点、开销>
        vector<vector<pair<int,int>>> edgs(N+1,vector<pair<int,int>>());
        //构建小顶堆,将最合适的点放在最前面
        priority_queue<vector<int>,vector<vector<int>>,cmp> pq;
        vector<int> visit(N+1, 0);
        //初始化边集合
        for(auto re : connections){
            edgs[re[0]].push_back(make_pair(re[1],re[2]));
            edgs[re[1]].push_back(make_pair(re[0],re[2]));
        }
        //本次选择1为起点,如果1点没有变,则1永远是孤岛。本次选择1为起点
        if(edgs[1].size() == 0){
            return -1;
        }

        selected = 1;
        visit[1] = true;
        //起点1所在的边放入小顶堆
        for(int i = 0;i < edgs[1].size(); ++i){
            pq.push(vector<int>({1,edgs[1][i].first,edgs[1][i].second}));
        }
       //遍历小顶堆
        while(!pq.empty()) {
            auto curr = pq.top();pq.pop();
            if(!visit[curr[1]]){
                visit[curr[1]] = true;
                ans += curr[2];
                 //依次取出cost最小的边所在的点加入集合
                for(auto e : edgs[curr[1]]){
                    pq.push(vector<int>({curr[1],e.first,e.second}));
                }
                selected++;
                if(selected == N){ //如果N个节点都在时,则结束循环
                    return ans;
                }
            }
        }
        return -1;
    }
};
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,667评论 5 472
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,361评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,700评论 0 333
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,027评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,988评论 5 361
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,230评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,705评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,366评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,496评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,405评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,453评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,126评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,725评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,803评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,015评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,514评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,111评论 2 341

推荐阅读更多精彩内容