题解 | #上司的舞会#
上司的舞会
https://www.nowcoder.com/practice/45c6d97dfd1044769aed5d9d3f139be1
题目链接
题目描述
公司有 名员工,构成一个多棵树组成的森林结构(即层级关系)。每位员工要么是最高层(没有上司),要么有且仅有一位直接上司。需要将所有员工分到若干小组,要求同一个小组内的任意两名员工之间不能有直接或间接的上级关系。求满足该条件的最小小组数量。
解题思路
这道题的核心在于理解分组的约束条件。
核心约束
“同一小组内不得出现任何一对员工具有上级关系。”
这句话是解题的关键。让我们考虑一条从上到下的指挥链,例如:A
是 B
的上司,B
是 C
的上司。
A
和B
不能在同一组。B
和C
不能在同一组。A
和C
也不能在同一组(因为A
是C
的间接上司)。 因此,A
、B
、C
这三位在同一指挥链上的员工,必须被分到三个不同的小组。
问题转化
由此我们可以推断,对于任意一条从最高层领导到基层员工的路径,路径上的所有员工都必须分在不同的小组里。如果这条路径上有 个员工,那么至少需要
个小组。
为了满足所有人的分组要求,我们需要的小组数量必须不少于最长指挥链上的员工人数。而这个“最长指挥链”的长度,正是在图论中树的高度(或深度)的定义。
由于员工的层级关系可能构成一个由多棵树组成的森林,我们需要找到这个森林中最高的那棵树。这棵树的高度,就是我们求解的最小小组数。
算法流程
-
建图:
- 根据输入的上司关系,我们可以构建一个图的邻接表。邻接表
adj[i]
存储了员工i
的所有直接下属。 - 在读入数据的同时,我们可以找出所有的最高层领导(即那些没有上司,输入为-1的员工),他们是森林中每一棵树的根节点。
- 根据输入的上司关系,我们可以构建一个图的邻接表。邻接表
-
计算每棵树的高度:
- 对于每一个根节点,我们可以通过深度优先搜索 (DFS) 来计算以它为根的树的高度。
- 一个递归函数
dfs(u)
可以计算并返回以节点u
为根的子树的高度。其逻辑是:u
的子树高度 = 1 (节点u
本身) +u
所有孩子节点子树中的最大高度。- 如果
u
是一个叶子节点(没有下属),则其子树高度为 1。
-
找出最大高度:
- 遍历所有的根节点,分别调用DFS计算它们的高度。
- 记录这些高度中的最大值。这个最大值就是最终的答案——满足条件的最小小组数。
代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
vector<int> adj[100005];
int dfs(int u) {
if (adj[u].empty()) {
return 1;
}
int max_child_depth = 0;
for (int v : adj[u]) {
max_child_depth = max(max_child_depth, dfs(v));
}
return 1 + max_child_depth;
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int n;
cin >> n;
vector<int> roots;
for (int i = 1; i <= n; ++i) {
int parent;
cin >> parent;
if (parent == -1) {
roots.push_back(i);
} else {
adj[parent].push_back(i);
}
}
int max_depth = 0;
for (int root : roots) {
max_depth = max(max_depth, dfs(root));
}
cout << max_depth << endl;
return 0;
}
import java.util.Scanner;
import java.util.ArrayList;
import java.util.List;
public class Main {
static List<Integer>[] adj;
public static int dfs(int u) {
if (adj[u].isEmpty()) {
return 1;
}
int maxChildDepth = 0;
for (int v : adj[u]) {
maxChildDepth = Math.max(maxChildDepth, dfs(v));
}
return 1 + maxChildDepth;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
adj = new ArrayList[n + 1];
for (int i = 1; i <= n; i++) {
adj[i] = new ArrayList<>();
}
List<Integer> roots = new ArrayList<>();
for (int i = 1; i <= n; i++) {
int parent = sc.nextInt();
if (parent == -1) {
roots.add(i);
} else {
adj[parent].add(i);
}
}
int maxDepth = 0;
for (int root : roots) {
maxDepth = Math.max(maxDepth, dfs(root));
}
System.out.println(maxDepth);
}
}
import sys
# 增加递归深度限制
sys.setrecursionlimit(100005)
def solve():
n_str = sys.stdin.readline()
if not n_str: return
n = int(n_str)
parents = list(map(int, sys.stdin.readline().split()))
adj = [[] for _ in range(n + 1)]
roots = []
for i, p in enumerate(parents):
employee_id = i + 1
if p == -1:
roots.append(employee_id)
else:
adj[p].append(employee_id)
memo = {}
def dfs(u):
if u in memo:
return memo[u]
if not adj[u]:
return 1
max_child_depth = 0
for v in adj[u]:
max_child_depth = max(max_child_depth, dfs(v))
memo[u] = 1 + max_child_depth
return memo[u]
max_depth = 0
for root in roots:
max_depth = max(max_depth, dfs(root))
print(max_depth)
solve()
算法及复杂度
-
算法:构建邻接表,深度优先搜索 (DFS) 求树的高度。
-
时间复杂度:
。
- 建图过程需要遍历所有
个员工,复杂度为
。
- DFS 会访问图中的每个节点和每条边一次。由于这是一个森林,边数为
,所以总的访问量是
。
- 总体时间复杂度为
。
- 建图过程需要遍历所有
-
空间复杂度:
。
- 邻接表需要
的空间来存储所有的层级关系。
- DFS 的递归调用深度在最坏的情况下(一条链)可能达到
,需要相应的栈空间。
- 邻接表需要