代码随想录第43天:图论4(最小生成树、拓扑排序)

一、冗余的边II(Kamacoder 109)

python 复制代码
from collections import defaultdict

# 并查集 - 查找根节点(路径压缩)
def find(fa, x):
    if fa[x] != x:
        fa[x] = find(fa, fa[x])
    return fa[x]

# 并查集 - 合并两个集合,返回是否合并成功
def union(fa, x, y):
    fx, fy = find(fa, x), find(fa, y)
    if fx != fy:
        fa[fx] = fy
        return True  # 合并成功,无环
    return False  # 已在一个集合中,成环

# 判断删除某一条边后,是否构成有向树(无环)
def is_tree_after_removal(edges, remove_idx, n):
    fa = list(range(n + 1))  # 初始化并查集
    for i, (u, v) in enumerate(edges):
        if i == remove_idx:  # 跳过指定要删除的边
            continue
        if not union(fa, u, v):  # 出现环
            return False
    return True

# 主函数:找出多余的那条边
def find_redundant_directed_connection(n, edges):
    in_deg = defaultdict(list)  # 记录每个节点的入边索引(用于找入度为2的点)

    for i, (u, v) in enumerate(edges):
        in_deg[v].append(i)

    # 情况 1:存在某个点入度为 2(有两个父节点)
    for v in in_deg:
        if len(in_deg[v]) == 2:
            i1, i2 = in_deg[v]
            # 优先删除后添加的边(尝试是否能变成树)
            if is_tree_after_removal(edges, i2, n):
                return edges[i2]
            # 否则只能删另一条
            return edges[i1]

    # 情况 2:无入度为2的点,仅存在有向环
    fa = list(range(n + 1))  # 初始化并查集
    for u, v in edges:
        if not union(fa, u, v):  # 出现环
            return [u, v]

# 读取输入和输出结果
if __name__ == "__main__":
    n = int(input())  # 节点数
    edges = [list(map(int, input().split())) for _ in range(n)]  # 读取边
    print(*find_redundant_directed_connection(n, edges))  # 输出多余的那条边

二、寻宝(Kamacoder 53)

最小生成树:在无向图中求一棵树(n-1条边,无环,连通所有点),而且这棵树的边权和最小。

等价于:怎么花最小成本连通无向图的所有点???

Prim(普里姆算法):更适合稠密图,与边的数量没关系,加点法

Kruskal(克鲁斯卡尔算法):更适合稀疏图,与节点的数量没关系,加边法

最小生成树可能不唯一,但是边权和唯一!!!!

python 复制代码
# 接收输入:v 个顶点,e 条边
v, e = list(map(int, input().strip().split()))

# 使用邻接矩阵初始化图:默认所有节点之间不可达,设为一个很大的数(10001)
graph = [[10001] * (v + 1) for _ in range(v + 1)]
for _ in range(e):
    x, y, w = list(map(int, input().strip().split()))
    graph[x][y] = w
    graph[y][x] = w  # 因为是无向图,双向赋值

# 初始化 Prim 算法所需变量
visited = [False] * (v + 1)  # 标记哪些节点已加入生成树
minDist = [10001] * (v + 1)  # 记录每个节点与当前生成树的最短连接边权重

# 从第一个节点开始构建 MST(通常默认从顶点1开始)
minDist[1] = 0  # 起点到自己权重为 0

# Prim 主循环:重复选择 v 次,每次选择一个未加入树的、距离最小的节点
for _ in range(1, v + 1):
    min_val = 10002  # 当前最小权重(初始为无穷大)
    cur = -1         # 当前选择加入树的节点

    # 遍历所有未访问的节点,选出与树连接距离最小的那个
    for j in range(1, v + 1):
        if not visited[j] and minDist[j] < min_val:
            cur = j
            min_val = minDist[j]

    visited[cur] = True  # 标记该节点已加入 MST

    # 更新其他未加入节点的 minDist,如果通过新加入的 cur 更近
    for j in range(1, v + 1):
        if not visited[j] and graph[cur][j] < minDist[j]:
            minDist[j] = graph[cur][j]

# 累加所有边权(minDist[1] 是起点设为 0,可以忽略)
ans = sum(minDist[2:])  # minDist[1] 为起点设为 0,不计入答案
print(ans)

三、软件构建(Kamacoder 117)

拓扑排序:所有点按照先后顺序排成序列,每次选入度为0的点,然后删除这个点和它的出边,如果拓扑排序进行不下去了,说明图中有环。

python 复制代码
from collections import deque, defaultdict

def topo_sort(n, edges):
    indeg = [0] * n  # indeg[i] 表示点 i 的入度(被多少个点指向)
    graph = defaultdict(list)  # 用邻接表 graph 存储有向图,graph[u] 里是所有从 u 出发可达的点

    # 构建图和入度数组
    for u, v in edges:
        graph[u].append(v)  # u 指向 v
        indeg[v] += 1       # v 的入度加一

    # 将所有入度为 0 的点入队,表示它们可以首先被处理
    q = deque(i for i in range(n) if indeg[i] == 0)

    res = []  # 存储拓扑排序的结果

    while q:
        u = q.popleft()   # 取出一个入度为 0 的点
        res.append(u)     # 加入结果中

        for v in graph[u]:  # 遍历 u 指向的所有节点
            indeg[v] -= 1   # u 被处理了,v 的入度减一
            if indeg[v] == 0:  # 若 v 入度减为 0,加入队列等待处理
                q.append(v)

    # 若结果中包含了所有 n 个节点,说明成功排序;否则图中有环,无法拓扑排序
    print(" ".join(map(str, res)) if len(res) == n else -1)

if __name__ == "__main__":
    # 读取顶点数 n 和边数 m
    n, m = map(int, input().split())
    
    # 读取 m 条边,构建边列表
    edges = [tuple(map(int, input().split())) for _ in range(m)]

    # 调用拓扑排序函数
    topo_sort(n, edges)
相关推荐
databook11 分钟前
Manim实现闪光轨迹特效
后端·python·动效
Juchecar1 小时前
解惑:NumPy 中 ndarray.ndim 到底是什么?
python
用户8356290780512 小时前
Python 删除 Excel 工作表中的空白行列
后端·python
Json_2 小时前
使用python-fastApi框架开发一个学校宿舍管理系统-前后端分离项目
后端·python·fastapi
数据智能老司机8 小时前
精通 Python 设计模式——分布式系统模式
python·设计模式·架构
数据智能老司机9 小时前
精通 Python 设计模式——并发与异步模式
python·设计模式·编程语言
数据智能老司机9 小时前
精通 Python 设计模式——测试模式
python·设计模式·架构
数据智能老司机9 小时前
精通 Python 设计模式——性能模式
python·设计模式·架构
c8i9 小时前
drf初步梳理
python·django
每日AI新事件9 小时前
python的异步函数
python