供水管线
kruskal算法(克鲁斯卡尔算法) ---- 最小生成树算法
应用场景:
从连通图中找出最小生成树,和实际相结合的有,水管共线,公交车站路线图,城市间修路等
算法主要思想:
将连通网中所有的边按照权值大小做升序排序,从权值最小的边开始选择,只要此边不和已选择的边一起构成环路,就可以选择它组成最小生成树。对于 N 个顶点的连通网,挑选出 N-1 条符合条件的边,这些边组成的生成树就是最小生成树。
算法难点:
“如何判断一个新边是否会和已选择的边构成环路?”
解决方案:
初始状态下,为连通网中的各个顶点配置不同的标记。对于一个新边,如果它两端顶点的标记不同,就不会构成环路,可以组成最小生成树。一旦新边被选择,需要将它的两个顶点以及和它直接相连的所有已选边两端的顶点改为相同的标记;反之,如果新边两端顶点的标记相同,就表示会构成环路。
简单来说就是使用辅助数组来记录顶点是否连通
题目代码题解
def main():
#code here
n,k = map(int,input().split())
# 这里我采用的是字典和元组存储顶点和边长,格式为为{(1,2):5,(1,2):3.....}
# 另外也可以使用类进行实现,但是我感觉会有些麻烦。不过思想都是统一的,喜欢使用那个就使用那个
dic = {
}
# 记录输入的数据
for i in range(k):
i,j,c = map(int,input().split())
if (i,j) in dic:
if dic[(i,j)] > c:
dic[(i,j)] = c
else:
dic[(i,j)] = c
# 排序,按照边的长度从小到大排序
new_dic = dict(sorted(dic.items(),key=lambda x:x[1]))
reslut = 0
#为每个顶点配置一个不同的标记,
assists = [i for i in range(n)]
for key,item in new_dic.items():
#找到当前边的两个顶点在 assists 数组中的位置下标
initial = key[0] -1
end = key[1]-1
num = 0 # 记录选择边的数量
# 如果顶点位置存在且顶点的标记不同,说明不在一个集合中,不会产生回路
if assists[initial] != assists[end]:
# 记录该边,作为最小生成树的组成部分,其实就是记录顶点和边,具体情况根据实际情况可以自己调整
# 在本题中题目只需要记录最低管理费用,所以只需要记录每次添加进的数据
reslut+=item
#将新加入生成树的顶点标记全部改为一样的 关键内容
elem = assists[end]
num+=1
for j in range(n):
if assists[j] == elem:
assists[j]= assists[initial]
#如果选择的边的数量和顶点数相差1,证明最小生成树已经形成,退出循环
if num == n-1:
break
print(reslut)
if __name__ == '__main__':
main();
参考文章:http://c.biancheng.net/algorithm/kruskal.html
补充:算法只是一种思想,需要根据实际情况进行合理的变形,不必过于纠结代码实现的方式,只要能实现无论使用那种方式都可以