算法训练营:道路升级
Posted syyyyy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法训练营:道路升级相关的知识,希望对你有一定的参考价值。
问题描述
Z国有 n 个城市和 m 条双向道路,每条道路连接了两个不同的城市,保证所有城市之间都可以通过这些道路互达。每条道路都有一个载重量限制,这限制了通过这条道路的货车最大的载重量。道路的编号从 1 至 m 。巧合的是,所有道路的载重量限制恰好都与其编号相同。
现在,要挑选出若干条道路,将它们升级成高速公路,并满足如下要求:
- 所有城市之间都可以通过高速公路互达。
- 对于任意两个城市 u,v 和足够聪明的货车司机:只经过高速公路从 u 到达 v 能够装载货物的最大重量,与经过任意道路从 u 到达 v 能够装载货物的最大重量相等。(足够聪明的司机只关注载重量,并不在意绕路)
在上面的前提下,要求选出的道路数目尽可能少。
求需要挑选出哪些道路升级成高速公路(如果有多种方案请任意输出一种)。
输入
第一行 2 个用空格隔开的整数 n,m ,分别表示城市数目、道路数目。
第 2 行到第 m+1 行,每行 2 个用空格隔开的整数 u,v 描述一条从 u 到 v 的双向道路,第 i+1 行的道路的编号为 i 。
注意:数据只保证不存在连接的城市相同的道路(自环),并不保证不存在两条完全相同的边(重边)
输出
第一行一个整数 k ,表示升级成高速公路的道路数。
接下来 k 行每行一个整数,从小到大输出所有选出的道路的编号。
输入样例
5 3 1 3 5 10 3
输出样例
2 2 3
数据范围
对于 20% 的数据,保证 n≤5,m≤10。
对于 60% 的数据,保证 n≤1,000,m≤5,000。
对于 100% 的数据,保证 n≤200,000,m≤400,000。
时间限制:10 sec
空间限制:256 MB
提示
[提示1:真的可能有多种方案吗?]
[提示2:k 是否一定为 n-1 呢?(也就是说,选出的道路是否恰好构成了一棵树?)]
[提示3:这道题和最小生成树有什么关系呢?]
代码实现:
1 #include <iostream> 2 #include <vector> 3 using namespace std; 4 5 // ================= 代码实现开始 ================= 6 7 const int N = 500005 ; 8 9 class UnionSet{ 10 public: 11 int f[N] ; 12 //初始化 13 //n :节点数量 14 void init( int n ) 15 { 16 for( int i = 1 ; i <= n ; i++ ) 17 f[i] = i ; 18 } 19 int find( int x ) 20 { 21 return f[x] == x ? x : f[x] = find( f[x] ) ; 22 } 23 //将x节点和y节点所在集合合并 24 // x:一个节点 25 // y : 一个结点 26 //返回值: 返回true表示成功合并,返回false表示已经在一个集合里了 27 bool merge( int x , int y ) 28 { 29 int setX = find(x) ; 30 int setY = find(y) ; 31 if( setX != setY ) 32 { 33 f[setX] = setY ; 34 return true ; 35 } 36 return false ; 37 } 38 }us; 39 40 // 给定一个n个点m条边的无向图,第i条边边权为i,求所有需要升级的边 41 // n:点->城市 42 // m:边->道路 43 // U:大小为m的数组,表示m条边的其中一个端点 44 // V:大小为m的数组,表示m条边的另一个端点 45 // 返回值:所有需要升级的边,从小到大排列;第一小问的答案自然即为返回值的size,所以你不必考虑如何返回size 46 47 vector<int>getAnswer(int n, int m, vector<int> U, vector<int> V) 48 { 49 vector<int> ans; 50 us.init(n); 51 //构造最大生成树,找权值最大的边 52 for( int i = m - 1 ; i >= 0 ; --i ) 53 if( us.merge( U[i] , V[i] ) ) 54 ans.push_back( i + 1 ) ; //加入边,从1开始到m 55 reverse(ans.begin() , ans.end()); //原来ans试从小到大排列的,现将ans数组从大到小排列 56 return ans; 57 } 58 59 int main() 60 { 61 int n, m; 62 scanf("%d%d", &n, &m); 63 vector<int> U, V; 64 for (int i = 0; i < m; ++i) { 65 int u, v; 66 scanf("%d%d", &u, &v); 67 U.push_back(u); 68 V.push_back(v); 69 } 70 vector<int> ans = getAnswer(n, m, U, V); 71 printf("%d\n", int(ans.size())); 72 for (int i = 0; i < int(ans.size()); ++i) 73 printf("%d\n", ans[i]); 74 return 0; 75 }
以上是关于算法训练营:道路升级的主要内容,如果未能解决你的问题,请参考以下文章
牛客网NowCoder 2018年全国多校算法寒假训练营练习比赛(第四场)A.石油采集(dfs) B.道路建设(最小生成树prim) C.求交集(暴力) F.Call to your teacher