📜  门| GATE-CS-2017(套装2)|第 52 题(1)

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

门| GATE-CS-2017(套装2)|第 52 题
问题描述

给定一个包含 n 个节点的有向图 G = (V,E),求一个大小为 k 的边集 F,使得对于 G的所有节点 u,u 到从 u 可以到达的节点 v 之间至少有 k 条边在集合 F 中。

输入格式
  • 第一行包含三个整数 n,m,k,分别表示节点的数量,边的数量和需要添加的边的数量。
  • 后面的m行描述边,每行包含两个整数u和v,表示从节点u到v的一条有向边。
  • 边从1到n编号。
输出格式
  • 如果有解,输出 F 的任意一个可行解,每行包含两个整数u和v,表示从节点u到v的一条有向边。
  • 如果无解,输出”-1”。
样例输入
4 4 2
1 2
2 3
3 1
3 4
样例输出
1 3
2 1
问题分析

这道题等价于给所有节点分别连一条长为k的链,最后把所有链合成一张图。如果原来的有向图是强连通的,那么合并得到的图也是强连通的,满足题目要求。如果有向图不强连通,那么合并后的图中至少有一个点是没法到达其他点的,无解。

算法描述
  1. 构建原图G;
  2. 对于每个点$u \in G$,构造一个长度为k的链$L$,如果从$u$出发能够到达L中的所有点,则加入到结果边集F中;
  3. 检查加入了多少条边,如果小于k,则无解;
代码实现
import queue

# 构建有向图
n, m, k = map(int, input().split())
graph = [[] for _ in range(n + 1)]
indegree = [0] * (n + 1)
for i in range(m):
    u, v = map(int, input().split())
    graph[u].append(v)
    indegree[v] += 1

# 拓扑排序求出起始点集合
start_nodes = []
q = queue.Queue()
for i in range(1, n + 1):
    if indegree[i] == 0:
        start_nodes.append(i)
        q.put(i)

# 对于每个起始点,往后跳k步的链是否存在
f = []
for s in start_nodes:
    L = set([s])
    for _ in range(k):
        new_L = set()
        for u in L:
            for v in graph[u]:
                new_L.add(v)
        L = new_L
    if len(L) < n:
        print("-1")
        exit(0)
    for u in L:
        f.append((s, u))

# 输出结果
for u, v in f:
    print(u, v)

代码解释:

  • 基于拓扑排序的算法,用队列q维护入度为0的节点集合;
  • 对于每个起始点,进行k次循环,对于集合L内的所有节点u,找到所有指向v的节点$v \in G_u$,并加入L中;
  • 检查L是否包含所有节点,如果不是,说明存在一个或多个节点到不了;

时间复杂度:$\mathcal{O}(mn+n^2)$

空间复杂度:$\mathcal{O}(n + m + k)$