📜  门| GATE-CS-2016(Set 1)|问题20(1)

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

门| GATE-CS-2016(Set 1)|问题20

本题是GATE-CS-2016(Set 1)的第20题,主要考察了程序员对于有向图的遍历和拓扑排序的掌握程度。

题目描述

一个城市公交车系统中,一些公交线路经过一些城市,并形成一个有向图。城市的编号使用 $1$ 到 $n$ 之间的整数表示,该图中有 $n$ 个节点和 $m$ 条边。每个节点上都有一张照片。当你保安检查过去时,你只能进入那些与你手中照片相同的城市。我们想确定有多少个城市可以被访问。可以假设图是强连通的。

函数签名如下:

def count_accessible_cities(n, m, edges, photos):
    pass
输入格式
  1. 第一行给定两个整数 $n$ 和 $m$,表示图中的节点数量和边的数量。
  2. 接下来 $m$ 行,每行给出两个整数 $x$ 和 $y$,表示从 $x$ 到 $y$ 存在一条有向边。
  3. 最后一行给出 $n$ 个整数 $p_{1},p_{2},\ldots,p_{n}$,其中 $p_{i}$ 表示编号为 $i$ 的城市上的照片编号。如果 $p_{i}$ 是 $0$,则表示编号为 $i$ 的城市上没有照片。( $1\leqslant n\leqslant 10^{5},0\leqslant m\leqslant 10^{6}$ 。)
输出格式

输出一个整数,表示有多少个城市可以被访问。

样例输入
5 5
1 2
2 3
3 1
3 4
5 3
1 0 0 0 5
样例输出
3
题目分析

本题要求我们在一个有向图中,找到能够被访问的城市数量,我们可以采用如下的算法:

  1. 对于所有照片编号不为 $0$ 的城市,采用深度优先搜索或广度优先搜索,找到所有能够被访问到的城市,并打上标记。
  2. 经过第一步之后,图中的所有被访问的城市都应该被打上了标记,没有被标记的节点就是无法被访问到的节点。除此之外,我们还需要考虑到在某些情况下,有些节点并不能通过直接搜索到达,但是因为存在一些其他的节点可以到达它,所以它可以间接被访问到。这种情况就需要采用拓扑排序来进行处理。如果一个节点的出度为 $0$,我们可以把它加入到队列中,并记录已经访问的节点数目。当所有的节点都已经访问时,队列中剩余的节点就是不可访问节点的集合。
代码实现
from collections import deque

def count_accessible_cities(n, m, edges, photos):
  
    visited = [False] * (n + 1)
    in_degree = [0] * (n + 1)
  
    # 遍历所有的边,并计算每个节点的入度
    for u, v in edges:
        in_degree[v] += 1
  
    # 把所有照片编号为 0 的城市的 visited 置为 True
    for i in range(1, n + 1):
        if photos[i - 1] == 0:
            visited[i] = True
          
    queue = deque([])
  
    # 将所有入度为 0 的节点加入队列中
    for i in range(1, n + 1):
        if in_degree[i] == 0:
            queue.append(i)
  
    visited_count = 0
  
    # 进行拓扑排序,更新 visited_count 的值
    while queue:
        u = queue.popleft()
        visited[u] = True
        visited_count += 1
  
        for v in range(1, n + 1):
            if not visited[v] and in_degree[v] > 0:
                in_degree[v] -= 1
                if in_degree[v] == 0:
                    queue.append(v)
  
    # 遍历图,更新 visited_count 的值
    for i in range(1, n + 1):
        if visited[i]:
            visited_count += 1
  
    return visited_count


# sample test
print(count_accessible_cities(5, 5, [(1, 2), (2, 3), (3, 1), (3, 4), (5, 3)], [1, 0, 0, 0, 5]))
复杂度分析

通过拓扑排序,我们每遍历一个节点一次,就可以把它从图中删除,并将与其相邻的节点入度减一。在最坏情况下,我们需要遍历图中所有的节点和边,时间复杂度为 $O(n+m)$。