统计为蚁群构筑房间的不同顺序
难度:
标签:
题目描述
你是一只蚂蚁,负责为蚁群构筑 n
间编号从 0
到 n-1
的新房间。给你一个 下标从 0 开始 且长度为 n
的整数数组 prevRoom
作为扩建计划。其中,prevRoom[i]
表示在构筑房间 i
之前,你必须先构筑房间 prevRoom[i]
,并且这两个房间必须 直接 相连。房间 0
已经构筑完成,所以 prevRoom[0] = -1
。扩建计划中还有一条硬性要求,在完成所有房间的构筑之后,从房间 0
可以访问到每个房间。
你一次只能构筑 一个 房间。你可以在 已经构筑好的 房间之间自由穿行,只要这些房间是 相连的 。如果房间 prevRoom[i]
已经构筑完成,那么你就可以构筑房间 i
。
返回你构筑所有房间的 不同顺序的数目 。由于答案可能很大,请返回对 109 + 7
取余 的结果。
示例 1:
输入:prevRoom
= [-1,0,1]
输出:1
解释:仅有一种方案可以完成所有房间的构筑:0 → 1 → 2
示例 2:
输入:prevRoom
= [-1,0,0,1,2]
输出:6
解释:
有 6 种不同顺序:
0 → 1 → 3 → 2 → 4
0 → 2 → 4 → 1 → 3
0 → 1 → 2 → 3 → 4
0 → 1 → 2 → 4 → 3
0 → 2 → 1 → 3 → 4
0 → 2 → 1 → 4 → 3
提示:
n == prevRoom.length
2 <= n <= 105
prevRoom[0] == -1
- 对于所有的
1 <= i < n
,都有0 <= prevRoom[i] < n
- 题目保证所有房间都构筑完成后,从房间
0
可以访问到每个房间
代码结果
运行时间: 1360 ms, 内存: 67.5 MB
// Problem Statement: Similar to the Java solution above, we want to find the number of different orders to construct rooms with constraints using Java Streams.
import java.util.*;
public class AntRoomConstructionStream {
private static final int MOD = 1000000007;
public int waysToBuildRooms(int[] prevRoom) {
int n = prevRoom.length;
List<List<Integer>> graph = new ArrayList<>();
Arrays.setAll(graph, ArrayList::new);
IntStream.range(1, n).forEach(i -> graph.get(prevRoom[i]).add(i));
long[] fact = new long[n + 1];
long[] invFact = new long[n + 1];
fact[0] = invFact[0] = 1;
IntStream.rangeClosed(1, n).forEach(i -> {
fact[i] = fact[i - 1] * i % MOD;
invFact[i] = pow(fact[i], MOD - 2);
});
return (int) dfs(graph, 0, fact, invFact)[1];
}
private long[] dfs(List<List<Integer>> graph, int node, long[] fact, long[] invFact) {
return graph.get(node).stream().mapToLong(next -> dfs(graph, next, fact, invFact)[0]).reduce(new long[]{1, 1}, (acc, x) -> {
acc[0] += x;
acc[1] = acc[1] * dfs(graph, node, fact, invFact)[1] % MOD * invFact[(int) x] % MOD;
return acc;
}, (x, y) -> x);
}
private long pow(long a, long b) {
long res = 1;
while (b > 0) {
if ((b & 1) == 1) res = res * a % MOD;
a = a * a % MOD;
b >>= 1;
}
return res;
}
}
解释
方法:
题解使用了树形 DP 的方法来解决问题。首先将房间之间的依赖关系转换为一棵树的形式,其中节点代表房间,边代表构建依赖。然后从根节点(房间0)开始进行深度优先搜索(DFS),在每个节点上计算构建该节点及其子树的所有不同顺序的数量。这个数量等于该节点所有子节点的顺序数量的乘积,再乘以该节点子树大小的阶乘,并且要考虑排列的顺序,所以还需要除以每个子节点子树大小的阶乘。
时间复杂度:
O(n)
空间复杂度:
O(n)
代码细节讲解
🦆
在构建依赖转换为树形结构时,如果数组`prevRoom`含有循环依赖(例如,房间3依赖房间4,而房间4又依赖房间3),该算法如何处理?
▷🦆
题解中提到使用深度优先搜索(DFS)计算不同顺序的数目,但未详细解释如何处理节点顺序和子树大小。请问`sz[u]`和`f[u]`数组分别在算法中起什么作用,它们是如何更新的?
▷🦆
在计算阶乘和逆元时,为什么选择使用`pow(fac[i], MOD - 2, MOD)`来计算逆元?这种方法的数学依据是什么?
▷