题目:繁忙的大都市
题目链接:https://ac.nowcoder.com/acm/problem/20245
题意:有n个交叉路口和m条双向道路,每条道路有边权。现在要改造道路,要求如下:
- 改造的道路要能把所有交叉路口直接或间接连通起来。
- 在1的前提下,改造的道路尽量少。
- 在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算法求解。
解题步骤:
- 将所有边按权值从小到大排序。
- 从小到大枚举所有边,若边的两个端点不连通,则选定该边,并合并两个边所在的集合。当选定的边数为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)。