代码随想录算法训练营 Day 49

1 minute read

Published:

最小生成树

最小生成树是所有节点的最小连通子图, 即:以最小的成本(边的权值)将图中所有节点链接到一起

Q1. KamaCoder 53

prim算法:

  1. 选距离生成树最近的节点
  2. 将节点加入到生成树中
  3. 更新非生成树节点到生成树的距离
if __name__ == '__main__':
    v, e = map(int, input().split())
    
    edge = [[10001] * (v + 1) for _ in range(v + 1)]
    for _ in range(e):
        n1, n2, weight = map(int, input().split())
        edge[n1][n2] = weight
        edge[n2][n1] = weight
    
    visited = [False] * (v + 1)
    min_dist = [10001] * (v + 1)
    
    for _ in range(1, v + 1):
        new_insert_node = -1
        min_val = float('inf')
        for j in range(1, v + 1):
            if not visited[j] and min_dist[j] < min_val:
                min_val = min_dist[j]
                new_insert_node = j
        visited[new_insert_node] = True
        
        for j in range(1, v + 1):
            if not visited[j] and min_dist[j] > edge[new_insert_node][j]:
                min_dist[j] = edge[new_insert_node][j]
    
    print(sum(min_dist[2:]))

Kruskal算法:判断是否在同一个集合中,就需要用到并查集

  1. 将所有的边按照权重进行排序
  2. 依次遍历所有边,如果边的两个顶点处于同一个集合中,说明不能加入这边,否则会形成环
  3. 如果边的两个顶点不处于同一个集合中,则将这条边加入到生成树中,并更新生成树中的权重和
class Edge:
    def __init__(self, source, destination, weight):
        self.s = source
        self.d = destination
        self.w = weight


class UnionFind:
    def __init__(self, size):
        self.parent = list(range(size + 1))
    
    def find(self, u):
        if u != self.parent[u]:
            self.parent[u] = self.find(self.parent[u])
        return self.parent[u]
    
    def union(self, u, v):
        root_u = self.find(u)
        root_v = self.find(v)
        if root_u != root_v:
            self.parent[root_v] = root_u


if __name__ == '__main__':
    v, e = map(int, input().split())
    
    edges = []
    for _ in range(e):
        n1, n2, weight = map(int, input().split())
        edges.append(Edge(n1, n2, weight))
    
    edges.sort(key=lambda x: x.w)
    uf = UnionFind(v)
    res = 0
    
    for edge in edges:
        x = uf.find(edge.s)
        y = uf.find(edge.d)
        if x != y:
            res += edge.w
            uf.union(x, y)
    
    print(res)

总结

prim 算法是维护节点的集合,而 Kruskal 是维护边的集合。因此稀疏图中,用Kruskal更优;在稠密图中,用prim算法更优。