📜  门|门CS 2011 |第 31 题(1)

📅  最后修改于: 2023-12-03 15:28:48.920000             🧑  作者: Mango

题目介绍

这是门清华大学计算机系2011年数据结构与算法课程设计题的第31题,也是一道经典的图论问题。

题目描述

在一个 $n$ 个节点的有向图中,一个点被称为“门”当且仅当该点没有入边但有至少一条出边。设计一个算法,计算出至少需要添加多少条有向边,使得所有节点都至少拥有一条入边,或确定无解。

解法思路

首先观察到,如果有一个点既没有入边又没有出边,那么这个点永远不可能拥有入边,因此可以把这个点和它所指向的点都删除,然后从剩下的点重新构建图进行分析。

考虑到需要给所有节点都添加一条入边,可以发现,如果一个点的出边指向另一个点,那么在这两个点之间连一条有向边是一定有益的,因为这样会使得包含这两个点的连通分量的入度全部增加1,而无需产生其他负面影响。

因此,本题可以使用拓扑排序或者DFS进行求解。具体的算法流程如下:

  1. 首先,统计出所有的“门”,并将其加入到队列(或栈)中。
  2. 然后,对于每一个加入队列的“门”,以其为起点进行一次广度(或深度)优先搜索,并将路径上的所有点全部标记。
  3. 最后,统计所有没有被标记的点的连通分量数,并将这些连通分量数减1即是答案。
代码实现

下面是使用DFS求解的Python代码实现:

visited = set()

def dfs(graph, node):
    visited.add(node)
    for next_node in graph[node]:
        if next_node not in visited:
            dfs(graph, next_node)

def min_edges_to_make_all_nodes_have_in_edge(n, edges):
    graph = {i: set() for i in range(n)}
    for a, b in edges:
        graph[a].add(b)
    doors = [node for node in range(n) if not graph[node] and any(node in neighbors for neighbors in graph.values() if neighbors != graph[node])]
    for door in doors:
        dfs(graph, door)
    connected_components = sum(1 for node in range(n) if node not in visited and dfs(graph, node) is None)
    return max(0, connected_components - len(doors))

上面的代码中,visited是一个集合,用来存储已经访问过的点;dfs()函数是一个递归的深度优先搜索函数,用来标记从一个特定的点出发可以到达的所有点;min_edges_to_make_all_nodes_have_in_edge()函数是主函数,用来执行算法的全部流程,并返回答案。

上面的代码实现也可以使用广度优先搜索而不是深度优先搜索来完成。