繁忙的都市(kruskal算法)

题目:繁忙的大都市

题目链接:https://ac.nowcoder.com/acm/problem/20245

题意:有n个交叉路口和m条双向道路,每条道路有边权。现在要改造道路,要求如下:

  1. 改造的道路要能把所有交叉路口直接或间接连通起来。
  2. 在1的前提下,改造的道路尽量少。
  3. 在1,2的前提下,改造的道路中最大的边权尽量小。

输入描述:

第一行给出交叉路口数n(1 <= n <= 300)和道路数m(1 <= m <= 10000)。

接下来m行,每行三个整数u, v, c表示交叉路口u和v之间有一条边权为c的无向边。

输出描述:

两个整数s, max。表示改造的道路的数目和改造的道路中的最大边权。

样例输入:

4 5

1 2 3

1 4 5

2 4 7

2 3 6

3 4 8

样例输出:

3 6

样例解释:选择第1,2,4三条边,边权分别为3,5,6,最大边权为6。

 

题目分析:题目要选定边使得所有点连通,且边数最少,且边权最大值尽量小,最小生成树刚好具有这些性质。可以使用kruskal算法求解。

解题步骤:

  1. 将所有边按权值从小到大排序。
  2. 从小到大枚举所有边,若边的两个端点不连通,则选定该边,并合并两个边所在的集合。当选定的边数为n – 1时,则说明此时已构成一棵树,此时的边权即为选定的边的最大边权。

AC代码:

#include<iostream>

#include<algorithm>

#include<vector>

 

using namespace std;

const int N     = 310, M = 10010;

struct st{

       int x, y, z;

       bool operator < (const st &X) const{

              return z < X.z;

       }

}a[M];

int n, m, parent[N];

 

int find(int x){

       if(x != parent[x]) parent[x] = find(parent[x]);

       return parent[x];

}

 

void Union(int x, int y){

       parent[find(x)] = find(y);

}

 

void solve(){

       scanf("%d %d", &n, &m);

       for(int i = 1;i <= n;i++) parent[i] = i;

       for(int i = 1;i <= m;i++) scanf("%d %d %d", &a[i].x, &a[i].y, &a[i].z);

       sort(a + 1, a + 1 + m);

      

       int cnt = 0, ans = 0;

       for(int i = 1;i <= m;i++){

              if(find(a[i].x) != find(a[i].y)){

                     Union(a[i].x, a[i].y);

                     ans = a[i].z;

                     cnt++;

                    

                     if(cnt == n - 1) break;

              }

       }

       printf("%d %d\n", n - 1, ans);

}

 

int main(void){

       solve();

      

       return 0;

}

时间复杂度:O(mlogm)。

空间复杂度:O(n + m)。

上一篇:webpack 之(24) entry配置详解


下一篇:mysql查询所有子节点 非递归