1. 问题描述:
某个局域网内有 n 台计算机和 k 条双向网线,计算机的编号是 1∼n。由于搭建局域网时工作人员的疏忽,现在局域网内的连接形成了回路,我们知道如果局域网形成回路那么数据将不停的在回路内传输,造成网络卡的现象。注意:对于某一个连接,虽然它是双向的,但我们不将其当做回路。本题中所描述的回路至少要包含两条不同的连接。两台计算机之间最多只会存在一条连接。不存在一条连接,它所连接的两端是同一台计算机。因为连接计算机的网线本身不同,所以有一些连线不是很畅通,我们用 f(i,j) 表示 i,j 之间连接的畅通程度,f(i,j) 值越小表示 i,j 之间连接越通畅。现在我们需要解决回路问题,我们将除去一些连线,使得网络中没有回路且不影响连通性(即如果之前某两个点是连通的,去完之后也必须是连通的),并且被除去网线的 Σf(i,j) 最大,请求出这个最大值。
输入格式
第一行两个正整数 n,k。接下来的 k 行每行三个正整数 i,j,m 表示 i,j 两台计算机之间有网线联通,通畅程度为 m。
输出格式
一个正整数,表示被除去网线的 Σf(i,j) 的最大值。
数据范围
1 ≤ n ≤ 100
0 ≤ k ≤ 200
1 ≤ f(i,j) ≤ 1000
输入样例:
5 5
1 2 8
1 3 1
1 5 3
2 4 5
3 4 2
输出样例:
8
来源:https://www.acwing.com/problem/content/description/1143/
2. 思路分析:
分析题目可以知道边的总和是一定的,要想使得删除的边的权重之和最大,那么应该使得留下的边的权重之和最小,并且删除边之后并不影响连通性,而且因为给出的图并不一定是联通的,所以本质上求解每一个连通块中的最小生成树,也即求解原图中的"最小生成森林",因为题目的输入的是每条边的权重所以使用kruskal算法求解会比较方便一点(prim算法每一次只能求解一个连通块,从连通块的起点向周围扩散所以需要多次调用prim算法,这样就不如kruskal算法方便了),可以发现最终得到的图不一定是联通的,因为每一个连通块都是相互独立的,所以kruskal算法求解每一个连通块的最小生成树的做法是正确的,求解出每一个连通块的中的最小生成树,那么剩余的就是需要删除的边,每一部分相互独立所以最终求解的答案一定是正确的。
3. 代码如下:
from typing import List
class Solution:
# 并查集查找x的父节点并且进行路径压缩
def find(self, x: int, fa: List[int]):
if x != fa[x]: fa[x] = self.find(fa[x], fa)
return fa[x]
def process(self):
n, m = map(int, input().split())
w = list()
for i in range(m):
a, b, c = map(int, input().split())
# 使用元组来存储边的信息方便后面排序
w.append((a, b, c))
# 这道题目是没有重边和自环的, 所以可以直接按照边权从小到大排序
w.sort(key=lambda x: x[2])
res = 0
# 并查集的父节点数组, 因为节点编号是从0开始的但是所以第一个元素也需要占一个坑
fa = [i for i in range(n + 1)]
for i in range(m):
a, b, c = w[i][0], w[i][1], w[i][2]
t1, t2 = self.find(a, fa), self.find(b, fa)
if t1 != t2:
fa[t1] = t2
# 需要删除当前的边所以需要累加到答案中
else: res += c
return res
if __name__ == "__main__":
print(Solution().process())