某测试工具在升级时总是选择迭代次数最多的补丁版本进行升级。
给定若干补丁版本之间的迭代关系:
每条关系表示“当前版本”是由“前序版本”修改得到的;
一个版本最多只有一个前序版本(即单链分支);
NA
表示该版本没有前序版本(即为初始版本);
不存在互为前序的情况(图为有向无环图 DAG);
请你返回最终可以升级的所有补丁版本,这些版本需要满足:
从起点版本开始到该版本的路径长度(迭代次数)最长;
若存在多个版本拥有相同的最大迭代次数,则按字典序升序输出。
第一行:一个整数 N(1 ≤ N ≤ 100000),表示补丁版本之间的迭代关系数量;
接下来 N 行:每行两个字符串,分别表示当前版本 和 它的前序版本,中间用空格隔开;
如果该版本没有前序版本,则第二个字符串为 NA。
输出迭代次数最多的所有版本号(可能多个),用空格隔开,按字典序升序排序。
6
A NA
B A
C A
D B
E C
F C
D E F
这个问题可以转化为 图的最长路径终点查找:
把所有补丁版本看成图中的节点;
每条版本迭代关系是一个有向边(从前序版本指向当前版本);
从入度为 0 的版本(前序为 NA)出发,使用 拓扑排序 + 动态规划 计算从起点到每个版本的最大迭代次数;
最终找出最大迭代次数的所有版本;
按字典序排序并输出。
import java.util.*;
public class PatchUpgrade {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = Integer.parseInt(scanner.nextLine()); // 读取关系条数
// 图的结构:当前版本 <- 前序版本
Map> graph = new HashMap<>();
Map inDegree = new HashMap<>(); // 记录每个节点的入度
Map dp = new HashMap<>(); // dp[version]:当前版本的最大迭代次数
Set allVersions = new HashSet<>(); // 所有出现过的版本
for (int i = 0; i < n; i++) {
String[] parts = scanner.nextLine().split(" ");
String cur = parts[0]; // 当前版本
String pre = parts[1]; // 前序版本
allVersions.add(cur);
// 如果不是NA,表示有前序版本
if (!pre.equals("NA")) {
// 建立前序版本 -> 当前版本 的映射
graph.computeIfAbsent(pre, k -> new ArrayList<>()).add(cur);
// 当前版本入度+1
inDegree.put(cur, inDegree.getOrDefault(cur, 0) + 1);
}
// 保证图中所有节点都初始化(避免空指针)
inDegree.putIfAbsent(cur, inDegree.getOrDefault(cur, 0));
dp.putIfAbsent(cur, 0);
dp.putIfAbsent(pre, 0); // NA 也初始化
}
// 使用队列进行拓扑排序
Queue queue = new LinkedList<>();
for (String version : allVersions) {
if (inDegree.getOrDefault(version, 0) == 0) {
queue.offer(version);
dp.put(version, 1); // 起始版本迭代次数为1
}
}
int maxIter = 1; // 记录最大迭代次数
// 拓扑排序 + 动态规划更新最长路径
while (!queue.isEmpty()) {
String cur = queue.poll();
int curIter = dp.get(cur); // 当前版本的最大迭代次数
// 遍历所有后继版本
for (String next : graph.getOrDefault(cur, new ArrayList<>())) {
// 动态规划:更新后继版本的迭代次数
dp.put(next, Math.max(dp.get(next), curIter + 1));
// 更新最大路径
maxIter = Math.max(maxIter, dp.get(next));
// 入度减1,如果入度为0加入队列
inDegree.put(next, inDegree.get(next) - 1);
if (inDegree.get(next) == 0) {
queue.offer(next);
}
}
}
// 收集所有最大迭代次数的版本
List result = new ArrayList<>();
for (String version : dp.keySet()) {
if (dp.get(version) == maxIter) {
result.add(version);
}
}
// 按字典序排序
Collections.sort(result);
// 输出
System.out.println(String.join(" ", result));
}
}
本题的核心是:拓扑排序找最长路径的末端节点。
与一般最长路径不同,我们只记录每个节点到源节点的最大深度(通过 DP 实现);
最后找出所有等于最大深度的节点,按字典序输出。