
题目描述
现在有n个容器服务,服务的启动可能有一定的依赖性(有些服务启动没有依赖),其次,服务自身启动加载会消耗一些时间。
给你一个n × n 的二维矩阵useTime,其中useTime[i][i]=10表示服务i自身启动加载需要消耗10s,useTime[i][j]=1表示服务i启动依赖服务j启动完成,useTime[i][k]=0表示服务i启动不依赖服务k。其中0 <= i, j, k < n。服务之间没有循环依赖(不会出现环),若想对任意一个服务i进行集成测试(服务i自身也需要加载),求最少需要等待多少时间。
输入描述
第一行输入服务总量n
,之后的n
行表示服务启动的依赖关系以及自身启动加载耗时,最后输入k
表示计算需要等待多少时间后,可以对服务k
进行集成测试,其中1 <= k <= n, 1 <= n <= 100
。
输出描述
最少需要等待多少时间(单位:秒)后,可以对服务k
进行集成测试。
示例1
输入:
3
5 0 0
1 5 0
0 1 5
3
输出:
15
说明:
服务3启动依赖服务2,服务2启动依赖服务1,由于服务1、2、3自身加载都需要消耗5s,所以5+5+5=15s,需要等待15s后可以对服务3进行集成测试。
示例2
输入:
3
5 0 0
1 10 1
1 0 11
2
输出:
26
说明:
服务2启动依赖服务1和服务3,服务3启动需要依赖服务1,服务1、2、3自身加载需要消耗5s、10s、11s,所以5+10+11=26s,需要等待26s后可以对服务2进行集成测试。
📌 题目分析
这道题涉及 有向无环图(DAG)上的最长路径问题 ,可以用 深度优先搜索(DFS)+ 记忆化搜索 来解决。
在这道题中:
- 节点 代表 容器服务。
- 边 代表 服务之间的依赖关系。
- 节点的值 代表 服务自身启动的耗时。
- 目标 是计算对某个服务
k
进行集成测试的最少等待时间。
转换思维 :
题目等价于计算从所有依赖的服务出发,沿依赖路径到
k
的 最长路径和。
🧠 解题思路
-
建模为有向无环图(DAG):
- 用
n × n
的 邻接矩阵useTime
表示 依赖关系 和 自身耗时。 useTime[i][j] = 1
表示i
依赖j
。useTime[i][i]
代表 服务i
自身的加载时间。
- 用
-
深度优先搜索(DFS)+ 记忆化搜索:
- 递归求解每个服务的最长路径 (即启动
k
之前所需的时间)。 - 递归时:
- 如果
i
依赖j
,则先计算j
的时间。 maxTime = max(所有依赖的时间) + 自身启动时间
。
- 如果
- 使用
memo
数组存储计算过的值,避免重复计算。
- 递归求解每个服务的最长路径 (即启动
-
最终答案:
- 计算从 所有依赖的服务出发到
k
的 最长路径时间。
- 计算从 所有依赖的服务出发到
💻 代码实现
✅ 代码优化 + 详细注释
javascript
const rl = require("readline").createInterface({ input: process.stdin });
var iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;
// 记忆化数组,避免重复计算
let memo = {};
// 计算启动服务 `i` 需要的最小等待时间(DFS + 记忆化)
function dfs(useTime, i) {
// 如果已经计算过,直接返回
if (memo[i] !== undefined) return memo[i];
let maxTime = 0;
for (let k = 0; k < useTime.length; k++) {
// `useTime[i][k] == 1` 表示 `i` 依赖 `k`,递归计算 `k`
if (useTime[i][k] === 1 && i !== k) {
maxTime = Math.max(maxTime, dfs(useTime, k));
}
}
// 计算当前服务 `i` 需要的时间 = 最大依赖时间 + 自身启动时间
memo[i] = maxTime + useTime[i][i];
return memo[i];
}
void async function () {
// 读取服务总数 `n`
const n = parseInt(await readline());
const useTime = [];
// 读取 `n × n` 的矩阵
for (let i = 0; i < n; i++) {
let row = (await readline()).split(' ').map(Number);
useTime.push(row);
}
// 读取 `k`,并转换为 0-based 索引
let k = parseInt(await readline()) - 1;
// 计算最少需要等待的时间
console.log(dfs(useTime, k));
rl.close();
}();
🚀 代码解析
-
数据读取
- 先读取
n
(服务数量)。 - 读取
n × n
的useTime
矩阵,表示服务依赖关系和自身启动时间。 - 读取
k
(需要计算的目标服务),并 转换为 0-based 索引。
- 先读取
-
深度优先搜索(DFS)+ 记忆化
- 维护
memo
数组 ,存储已经计算过的时间,避免重复计算(动态规划思想)。 - 遍历
useTime[i][k] == 1
的依赖项,递归计算其 最长路径和。 maxTime = max(所有依赖的时间) + 自身启动时间
。
- 维护
-
最终输出
console.log(dfs(useTime, k));
计算k
需要的最少等待时间。
📊 复杂度分析
复杂度 | 说明 |
---|---|
时间复杂度:O(n²) | 由于是 DAG(无环图),每个服务最多遍历 n 次,每个 dfs() 调用最多递归 n 层,因此最坏情况是 O(n²) 。 |
空间复杂度:O(n) | 主要存储 memo 数组 和递归栈,最坏情况下 O(n) 。 |
✅ 测试 & 运行示例
📌 示例 1
🔹 输入:
3
5 0 0
1 5 0
0 1 5
3
🔹 计算过程:
服务 3 依赖 服务 2
服务 2 依赖 服务 1
服务 1 自身耗时 5s
服务 2 自身耗时 5s
服务 3 自身耗时 5s
总时间 = 5 + 5 + 5 = 15s
🔹 输出:
15
📌 示例 2
🔹 输入:
3
5 0 0
1 10 1
1 0 11
2
🔹 计算过程:
服务 2 依赖 服务 1、服务 3
服务 3 依赖 服务 1
服务 1 自身耗时 5s
服务 3 自身耗时 11s(依赖 服务 1)
服务 2 自身耗时 10s(依赖 服务 1、服务 3)
最长路径 = 5 + 11 + 10 = 26s
🔹 输出:
26
🔎 总结
方法 | 特点 | 适用场景 |
---|---|---|
DFS + 记忆化 | 递归遍历 DAG,存储计算结果,避免重复计算 | 适用于 有向无环图(DAG)上的最长路径 |
拓扑排序 + 动态规划(DP) | 依赖 入度计算 ,从 无依赖的节点 逐步计算 | 适用于 大规模 DAG 依赖关系 |
✅ 方案优势
- 使用
DFS + 记忆化
,避免重复计算,优化O(n²)
。 - 代码简洁,符合递归求最长路径的思路。
- 无环保证,直接计算最长路径,无需拓扑排序。
🔚 结论
这道题本质上是 有向无环图(DAG)上的最长路径问题 ,可以用 DFS + 记忆化 优化。通过递归计算 最长依赖链的时间 ,最终求出 启动服务 k
需要的最少时间。
✨ 如果数据规模更大(如 n > 10^5
),可以考虑拓扑排序+DP 解法! 🚀
整理题解不易, 如果有帮助到您,请给点个赞 ❤️ 和收藏 ⭐,让更多的人看到。🙏🙏🙏