LeetCode——1916. 统计为蚁群构筑房间的不同顺序[Count Ways to Build Rooms in an Ant Colony][困难]——分析及代码[Java]
一、题目
你是一只蚂蚁,负责为蚁群构筑 n 间编号从 0 到 n-1 的新房间。给你一个 下标从 0 开始 且长度为 n 的整数数组 prevRoom 作为扩建计划。其中,prevRoom[i] 表示在构筑房间 i 之前,你必须先构筑房间 prevRoom[i] ,并且这两个房间必须 直接 相连。房间 0 已经构筑完成,所以 prevRoom[0] = -1 。扩建计划中还有一条硬性要求,在完成所有房间的构筑之后,从房间 0 可以访问到每个房间。
你一次只能构筑 一个 房间。你可以在 已经构筑好的 房间之间自由穿行,只要这些房间是 相连的 。如果房间 prevRoom[i] 已经构筑完成,那么你就可以构筑房间 i。
返回你构筑所有房间的 不同顺序的数目 。由于答案可能很大,请返回对 10^9 + 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 <= 10^5
- prevRoom[0] == -1
- 对于所有的 1 <= i < n ,都有 0 <= prevRoom[i] < n
- 题目保证所有房间都构筑完成后,从房间 0 可以访问到每个房间
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/count-ways-to-build-rooms-in-an-ant-colony
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
二、分析及代码
1. 动态规划
(1)思路
对每个节点,可根据所有以其子节点为根的树的节点及排列数量,计算出以当前节点为根的树的节点及排列数量。
本题求解过程涉及较多前置知识点,包括排列组合、乘法逆元、快速乘方等,可参考官方题解。
(2)代码
class Solution {
int mod = (int)1e9 + 7;
Map<Integer, List<Integer>> edges = new HashMap<>();
int[] fac, inv;
public int waysToBuildRooms(int[] prevRoom) {
int n = prevRoom.length;
//求阶乘数列及对应逆元
this.fac = new int[n];//fac[i]=i!
this.inv = new int[n];//inv[i]=i!^(-1)
fac[0] = inv[0] = 1;
for (int i = 1; i < n; i++) {
fac[i] = (int)((long)fac[i - 1] * i % mod);
inv[i] = quickmul(fac[i], mod - 2);//费马小定理, (fac[i]^(-1))%mod = (fac[i]^(mod-2))%mod
}
//记录各个节点与子节点之间的边
for (int i = 1; i < n; i++) {
if (edges.containsKey(prevRoom[i]) == false)
edges.put(prevRoom[i], new ArrayList<Integer>());
edges.get(prevRoom[i]).add(i);
}
//动态规划得到总体顺序数量
return dfs(0)[1];
}
//深度优先搜索,返回以当前节点为根的子树节点个数 及 内部排列数
public int[] dfs(int node) {
if (edges.containsKey(node) == false)//子节点,节点个数及内部排列数均为1
return new int[]{1, 1};
int count = 1, arr = 1;//子树的节点个数、内部排列数
for (int v : edges.get(node)) {
int[] ret = dfs(v);//递归得到子节点对应树的节点个数和排列数
count += ret[0];
arr = (int)((long)arr * ret[1] % mod * inv[ret[0]] % mod);
}
arr = (int)((long)arr * fac[count - 1] % mod);
return new int[]{count, arr};
}
//快速计算x^y的乘方
public int quickmul(int x, int y) {
long ret = 1, cur = x;
while (y > 0) {
if ((y & 1) == 1)
ret = ret * cur % mod;
cur = cur * cur % mod;
y >>= 1;
}
return (int)ret;
}
}
(3)结果
执行用时 :684 ms,在所有 Java 提交中击败了 61.54% 的用户;
内存消耗 :576.8 MB,在所有 Java 提交中击败了 5.77% 的用户。
三、其他
暂无。