Datahub
数据改变生活

道路升级

发表时间:2022-10-27 20:16

道路升级

问题描述

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 行每行一个整数,从小到大输出所有选出的道路的编号。

输入样例

输样例

数据范围

对于 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:这道题和最小生成树有什么关系呢?]

代码

#include <iostream> #include<vector>

#pragma warning(disable:4996) using namespace std;

// ================= 代码实现开始 =================

/* 请在这里定义你需要的全局变量 */ int *Father;

int Find(int x)//查找某节点的根节点

{

int fx;//某节点的根节点(父节点)

if (Father[x] > 0)//若x的秩大于0,说明x不是根节点

{

fx = Find(Father[x]);//若x不是根节点,则递归查找其根节点

Father[x] = fx;//*****此处至关重要,路径压缩,将某节点的父节点设置为根节点

********

return fx;

}

else

return x;//若x的秩小于等于0,说明x是根节点,返回x

}

void Union(int x, int y)//将x和y所在的两个集合合并

{

int fx = Find(x);

int fy = Find(y);

fy = fy;//断点调试点

if (fx == fy && fx != 0)//若fx和fy相等且其两个的秩不为0,则说明他们在同一个集合树中

return;

if (Father[fx] < Father[fy])

Father[fy] = fx;//若x所在的集合树比较高,则将y所在的树合并到x所在的树中,即将y

的根节点设置为fx

else if (Father[fx] == Father[fy])//若两个树的高度相等,则任意将一个树合并到另一个树中(此处是将x的根节点设置为fy)

{

Father[fy] -= 1;//合并后树的高度加一,故秩减一

Father[fx] = fy;

}

else//若y所在的集合树比较高,则将x所在的树合并到y所在的树中,即将x的根节点设置为fy

Father[fx] = fy;

}

// 给定一个n个点m条边的无向图,第i条边边权为i,求所有需要升级的边

// n:如题意

// m:如题意

// U:大小为m的数组,表示m条边的其中一个端点

// V:大小为m的数组,表示m条边的另一个端点

// 返回值:所有需要升级的边,从小到大排列;第一小问的答案自然即为返回值的size,所以你不必考虑如何返回size

vector<int> getAnswer(int n, int m, vector<int> U, vector<int> V) {

/* 请在这里设计你的算法 */

//通过生成最大生成树,我们即可知道哪一些道路应当升级为高速公路

Father = new int[n + 1];

for (int j = 0; j < n + 1; j++)

{

Father[j] = 0;//每个元素的初始秩为0

}

vector<int> Answer;//输出

Answer.reserve(200000);

int k = 0;//应升级为高速公路的k条道路,由最大生成树可知,当k=n-1时最大生成树生成完毕

for(int j=m;j>0;--j)

{

int u = U.back();

U.pop_back();

int v = V.back();

V.pop_back();

int fu = Find(u);

int fv = Find(v);

//下面用城市序号生成最大生成树

if (fu != fv || fu == 0)//若两个城市的

{

Union(u, v);

Answer.emplace_back(j);//将需要升级的道路序号压入容器中

k = k;

k++;

}

if (k == n - 1)

break;

}

return Answer;

}

// ================= 代码实现结束 =================

int main() {

int n, m;

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

vector<int> U, V;

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

int u, v;

scanf("%d%d", &u, &v);

U.emplace_back(u);

V.emplace_back(v);

}

vector<int> ans = getAnswer(n, m, U, V);

printf("%d\n", int(ans.size()));

for (int i = int(ans.size())-1; i >= 0; --i)//由小到大输出道路序号

printf("%d\n", ans[i]);

return 0;

}


上一篇分组
下一篇等式
文章分类: 数据结构学习
分享到:
QQ:258506508                                     联系电话:020-000000    000-000000                                   联系邮箱:xxx@.co.m                                     联系地址:XXX省XXX市XXX县XXX路