2 solutions
-
0
from collections import deque, defaultdict #这个输出模式出度都是1,所以不需要再建图了 # 读取输入并初始化数据 n = int(input()) edges = list(map(int, input().split())) # 初始化每个节点的入度数组和子节点数量数组 in_degree = [0] * n nums = [0] * n # 更新每个节点的入度 for i in range(n): in_degree[edges[i]] += 1 # 队列用于存储所有入度为0的节点 q = deque() # 将所有入度为0的节点加入队列 for i in range(n): if in_degree[i] == 0: q.append(i) # BFS遍历 while q: cur_idx = q.popleft() cur_next_idx = edges[cur_idx] in_degree[cur_next_idx] -= 1 # 更新每个节点的子节点数目 nums[cur_next_idx] += nums[cur_idx] + 1 if in_degree[cur_next_idx] == 0: q.append(cur_next_idx) # 存储所有环的列表 circles = [] # 存储每个环的内聚值 H = L(环内节点数量) - V(指向环的节点数量) h_val = [] # 存储每个环中的最大节点索引 max_idx_in_circles = [] # 找到环 for i in range(n): if in_degree[i] == 0: continue cur_idx = i v = 0 max_idx = i cur_circle = [] # 遍历找到环 while in_degree[cur_idx] > 0: v += nums[cur_idx] cur_circle.append(cur_idx) in_degree[cur_idx] = 0 cur_idx = edges[cur_idx] max_idx = max(max_idx, cur_idx) circles.append(cur_circle) max_idx_in_circles.append(max_idx) # 计算内聚值,并加入列表 h_val.append(len(cur_circle) - v) # 对环进行排序 idx = list(range(len(h_val))) # 对索引数组进行排序,优先根据内聚值排序,其次看最大节点索引 idx.sort(key=lambda a: (-h_val[a], -max_idx_in_circles[a])) # 找到最好的环 res_circle = circles[idx[0]] # 按照顺序输出从最小节点开始的环 start_idx = min(res_circle) print(start_idx) for i in range(len(res_circle)): print(start_idx, end=" " if i != len(res_circle) - 1 else "\n") start_idx = edges[start_idx]
-
0
题面描述:
在这道题中,我们需要处理一个包含
n
个微服务的调用关系数组edges
,其中每个微服务通过数组值指向另一个微服务。我们的目标是识别所有的微服务环群组,计算每个环的内聚值 H = L - V(L 为环中微服务的数量,V 为可以访问该环的其他微服务数量),并根据 H 值进行排序(当 H 值相同时,按环中最大微服务编号排序)。最后,输出 H 值最大的环,确保输出顺序是从环中编号最小的微服务开始。输入包含一个整数n
和一个长度为n
的数组edges
,输出为满足条件的微服务编号,以空格分隔。思路:BFS遍历
1,6,3组成了微服务群组(环) a1,L1值为3,编号为4、9的2个微服务可以访问到a1,因此√1值为2,H1为L1V1 =1;
0,2,10,5组成了微服务群组 (环) a2,L2值为4,编号为7、8、11的3个微服务可以访问到2,因此v2值为3,H2为L2-V2=1;
先对比H值,H1=H2,H值相等;
再对比环中序号最大值,a1中最大数为6。a2中最大数为10,a2排前面,因此输出答案为:0 2 10 5
题解
本题要求识别微服务调用中的环(强连通分量),并计算它们的内聚值。具体步骤如下:
-
输入数据结构:首先,我们需要一个整数
n
表示微服务数量,以及一个数组edges
,其中edges[i]
表示微服务i
调用的目标微服务。 -
图的表示:将微服务调用关系表示为有向图。每个微服务通过调用关系指向其他微服务。
-
强连通分量:利用 Tarjan 算法找出所有的强连通分量(SCC),每个强连通分量内的节点可以互相到达。
-
计算内聚值:
- 对于每个强连通分量,计算环内微服务数量 L 和可访问该环的其他微服务数量 V,从而得到内聚值 H = L - V。
- H 值越大,表示环的内聚性越强。
-
排序和输出:根据 H 值从大到小排序,若 H 值相等,则选择环中编号最大的微服务进行比较。最终输出 H 值最大的环,且按照环中最小编号的微服务开始输出。
代码
C++
#include <bits/stdc++.h> using namespace std; const int maxn = 2e5 + 10; // 最大微服务数量 int n, m; // n: 微服务数量, m: 边的数量(暂未使用) int a[maxn]; // 存储微服务调用关系 int ver[maxn << 1], Next[maxn << 1], head[maxn], tot = 1; // 图的边信息 int low[maxn], dfn[maxn], num; // Tarjan算法中的访问标记和低点 int Stack[maxn], top, ins[maxn], c[maxn]; // Tarjan算法的栈和标记 int cnt, id, ans = INT_MIN; // cnt: 强连通分量数量, id: 当前最大H值的强连通分量编号 vector<int> scc[maxn]; // 存储所有强连通分量 // 添加边到邻接表 inline void add(int x, int y) { ver[++tot] = y; Next[tot] = head[x]; head[x] = tot; } // Tarjan算法实现 void tarjan(int x) { dfn[x] = low[x] = ++num; // 设置访问时间和最低点 Stack[++top] = x; // 将当前节点入栈 ins[x] = 1; // 标记当前节点在栈中 for (int i = head[x]; i; i = Next[i]) { int y = ver[i]; // 访问邻接点 if (!dfn[y]) { // 如果y未被访问 tarjan(y); // 递归访问 low[x] = min(low[x], low[y]); // 更新最低点 } else if (ins[y]) { low[x] = min(low[x], dfn[y]); // 更新最低点 } } // 强连通分量的根节点 if (low[x] == dfn[x]) { ++cnt; // 新的强连通分量 int y; do { y = Stack[top--]; // 出栈 ins[y] = 0; // 标记出栈 c[y] = cnt; // 记录所属强连通分量 scc[cnt].push_back(y); // 存储强连通分量 } while (x != y); // 直到回到根节点 } } // 获取强连通分量中的最大编号 int getMax(int id) { int res = 0; for (int x : scc[id]) { res = max(res, x); // 比较取最大 } return res; } // 计算访问到达某个强连通分量的数量 int dfs(int x, int fa) { int tmp = 0; for (int i = head[x], y; i; i = Next[i]) { y = ver[i]; if (y == fa) continue; // 不回到父节点 tmp += dfs(y, x); // 递归访问 } return tmp + 1; // 返回访问到的节点数 } int main() { std::ios::sync_with_stdio(false); // 加速输入输出 cin >> n; // 输入微服务数量 // 构建图 for (int i = 0; i < n; ++i) { cin >> a[i]; // 输入每个微服务的调用关系 add(i, a[i]); // 添加边 } // 使用Tarjan算法求解强连通分量 for (int i = 1; i <= n; ++i) { if (!dfn[i]) { tarjan(i); } } // 新建反图的构建 for (int i = 0; i < n; ++i) { if (scc[c[i]].size() == 1) { // 该强连通分量只有一个点 add(c[a[i]] + n, c[i] + n); // 只有不在环中的点才需要建立边 } } // 遍历所有强连通分量 for (int i = 1; i <= cnt; ++i) { if (scc[i].size() != 1) { // 说明为环 int V = dfs(i + n, -1) - 1; // 计算可访问的节点数量 int H = scc[i].size() - V; // 计算内聚值 if (H > ans) { // 更新最大内聚值 id = i; ans = H; } else if (H == ans && getMax(id) < getMax(i)) { // H相等时,比较最大编号 id = i; } } } // 找到该强连通分量中编号最小的点的位置 int pos = 0; for (int i = 0; i < scc[id].size(); ++i) { if (scc[id][i] < scc[id][pos]) { pos = i; // 更新最小编号的位置 } } // 求scc时,编号按遍历顺序倒着加入进scc中,所以倒序输出 for (int i = pos; i >= 0; --i) { cout << scc[id][i] << " "; // 输出环中节点 } for (int i = scc[id].size() - 1; i > pos; --i) { cout << scc[id][i] << " "; // 输出环中节点 } return 0; }
python
# 输入微服务数量 n = int(input()) # 输入调用关系 to = list(map(int, input().split())) # 初始化并查集(Union-Find)的父节点数组 f = list(range(n)) # 初始化每个微服务的入度计数 d = [0] * n # 查找函数,路径压缩 def find(x): if x != f[x]: # 如果 x 不是根节点 f[x] = find(f[x]) # 递归查找并进行路径压缩 return f[x] # 返回根节点 # 遍历每个微服务的调用关系 for i, v in enumerate(to): f[find(i)] = find(v) # 合并并查集 d[v] += 1 # 增加目标微服务的入度 from collections import deque # 初始化队列,存储入度为0的微服务 q = deque() for i in range(n): if d[i] == 0: # 找到没有入度的微服务 q.append(i) # 将其加入队列 # 处理队列中的微服务 while q: u = q.popleft() # 从队列中取出微服务 d[to[u]] -= 1 # 减少目标微服务的入度 if d[to[u]] == 0: # 如果入度为0,则加入队列 q.append(to[u]) # 使用集合存储所有的根节点(强连通分量) s = set() for i in range(n): s.add(find(i)) # 通过查找获取根节点并添加到集合 # 映射根节点到索引 mp = {} for i, v in enumerate(s): mp[v] = i # 为每个根节点分配一个唯一索引 # 初始化每个强连通分量的信息:数量L, 可访问数量V, 最大编号, 最小编号 a = [[0] * 4 + [float("inf")] for _ in range(len(s))] for i in range(n): index = mp[find(i)] # 找到当前微服务所在的强连通分量 if d[i]: # 如果当前微服务有入度 a[index][0] += 1 # L值增加 a[index][2] = max(a[index][2], i) # 更新最大编号 a[index][3] = min(a[index][3], i) # 更新最小编号 else: a[index][1] += 1 # V值增加 # 根据内聚值 H = L - V 和最大编号排序 a.sort(key=lambda x: (-(x[0] - x[1]), -x[2])) # 选择内聚值最高的强连通分量,输出起始编号 start = a[0][3] # 取最小编号作为起始 res = [start] # 结果列表,初始包含起始编号 s = {start} # 集合用于记录已访问的微服务 # 遍历当前环,直到回到已访问的微服务 while True: start = to[start] # 移动到下一个微服务 if start in s: # 如果已经访问过,则终止 break res.append(start) # 添加当前微服务 s.add(start) # 记录当前微服务为已访问 # 输出结果,按照要求的格式打印 print(' '.join(str(x) for x in res))
Java
import java.util.*; public class Main { static int n; static int[] edges; static int[] nums; // 每个节点的子节点数目 static int[] inDegree; public static void main(String[] args) { // Input Scanner sc = new Scanner(System.in); n = sc.nextInt(); edges = new int[n]; // 存储每个节点指向的下一个节点的数组 inDegree = new int[n]; // 存储每个节点的入度数组 nums = new int[n]; // 每个节点的子节点数目 // 读取每个节点的下一个节点,并更新入度数组 for (int i = 0; i < n; ++i) { edges[i] = sc.nextInt(); // 读取每个节点的下一个节点 inDegree[edges[i]]++; } Queue<Integer> q = new LinkedList<>(); // 创建队列以存储所有入度为0的节点 // 将所有入度为0的节点加入队列 for (int i = 0; i < n; ++i) { if (inDegree[i] == 0) { q.offer(i); } } // BFS遍历 while (!q.isEmpty()) { int qSize = q.size(); for (int i = 0; i < qSize; i++) { int curIdx = q.poll(); int curNextIdx = edges[curIdx]; inDegree[curNextIdx]--; // 当前节点指向的结点的入度减1 // nums 存储每个节点的子节点数目 nums[curNextIdx] += nums[curIdx] + 1; if (inDegree[curNextIdx] == 0) { q.offer(curNextIdx); } } } List<List<Integer>> circles = new ArrayList<>(); // 存储所有环的列表 List<Integer> hVal = new ArrayList<>(); // 存储每个环的内聚值H = L(环内节点数量) - V(指向环的节点数量) List<Integer> maxIdxInCircles = new ArrayList<>(); // 存储每个环中的最大节点索引 // 找环 for (int i = 0; i < n; i++) { if (inDegree[i] == 0) { continue; } int curIdx = i, v = 0, maxIdx = i; List<Integer> curCircle = new ArrayList<>(); while (inDegree[curIdx] > 0) { // nums 存储每个节点的子节点数目 v += nums[curIdx]; curCircle.add(curIdx); inDegree[curIdx] = 0; curIdx = edges[curIdx]; maxIdx = Math.max(maxIdx, curIdx); } circles.add(curCircle); maxIdxInCircles.add(maxIdx); // 计算内聚值,并加入列表 // 每个环的内聚值H = L(环内节点数量) - V(指向环的节点数量) hVal.add(curCircle.size() - v); } Integer[] idx = new Integer[hVal.size()]; // 创建索引数组 for (int i = 0; i < idx.length; ++i) idx[i] = i; // 初始化索引数组 // 对索引数组进行排序,优先根据环的内聚值排序,其次看最大节点索引 Arrays.sort(idx, (a, b) -> { int valCompare = hVal.get(b).compareTo(hVal.get(a)); if (valCompare != 0) return valCompare; return Integer.compare(maxIdxInCircles.get(b), maxIdxInCircles.get(a)); }); List<Integer> resCircle = circles.get(idx[0]); int startIdx = Collections.min(resCircle); for (int i = 0; i < resCircle.size(); i++) { System.out.print(startIdx); startIdx = edges[startIdx]; if (i != resCircle.size() - 1) { System.out.print(" "); } } } }
-
- 1
Information
- ID
- 91
- Time
- 1000ms
- Memory
- 256MiB
- Difficulty
- 7
- Tags
- # Submissions
- 192
- Accepted
- 31
- Uploaded By