题目:
告警抑制,是指高优先级抑制低优先级告警的规则 。高优先级告警产生后,低优先级告警不再产生。请根据原始告警列表和告警抑制关系,给出实际产生的告警列表。
注意:不会出现循环抑制的情况。
告警不会传递,比如 A>B,B>C 这种情况下A不会直接抑制C。 但被抑制的告警仍然可以抑制其他低优先级的告警。
输入描述:
第一行位数字N,表示告警抑制关系个数,0<=N<=120
接下来N行,每行是由空格分隔的两个告警ID,例如:id1 id2,表示id1抑制id2,告警ID的格式为大写字母+0个或者1个数字。 最后一行为告警产生列表,列表长度[1,100].
输出描述:
真实产生的告警列表
输入举例:
2
A B
B C
A B C D E
输出为:
A D E
说明A抑制了B,B抑制了C,最后实际告警为 A D E
解题思路:
- 首先,需要将告警抑制关系用一个图来表示。图中的点表示告警,如果告警A可以抑制告警B,那么在A和B之间连一条有向边。因为告警不会传递,所以这是一个有向无环图(DAG)。
- 其次,需要将原始告警列表中被抑制的告警全部删除。具体做法是从仅有入度的节点(即没有被任何其他节点抑制的节点)开始,沿着有向边一直走到叶子节点,把路径上所有的节点都标记为被删除。这个过程可以用拓扑排序来实现。
- 最后,输出没有被删除的节点即可。
Java:
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt(); // 告警抑制关系个数
sc.nextLine();
Map<String, Set<String>> graph = new HashMap<>(); // 告警抑制关系图
Set<String> nodes = new HashSet<>(); // 所有告警节点
for (int i = 0; i < n; i++) {
String[] parts = sc.nextLine().split(" ");
String from = parts[0], to = parts[1];
// 添加节点
nodes.add(from);
nodes.add(to);
// 添加有向边
graph.computeIfAbsent(from, k -> new HashSet<>()).add(to);
}
String[] alerts = sc.nextLine().split(" "); // 原始告警列表
Set<String> toDelete = new HashSet<>(); // 待删除的节点
// 拓扑排序
Queue<String> queue = new LinkedList<>();
// 找到所有入度为0的节点
Set<String> indegrees = new HashSet<>(nodes);
for (Set<String> edges : graph.values()) {
indegrees.removeAll(edges);
}
queue.addAll(indegrees);
while (!queue.isEmpty()) {
String node = queue.poll();
for (String neighbor : graph.getOrDefault(node, Collections.emptySet())) {
// 如果这个节点还没有被删除,才将其度数减1
if (!toDelete.contains(neighbor)) {
toDelete.add(neighbor);
queue.offer(neighbor);
}
}
}
// 输出剩下的节点
for (String alert : alerts) {
if (!toDelete.contains(alert)) {
System.out.print(alert + " ");
}
}
}
}