文章目录
拓扑排序
- 拓扑排序,常用于这个课程表安排类型的,对于一个抽象为一个
有向无环图
,经过排序之后,排序之后的元素x,y
,仍然保持着在原来的有向无环图当中,x->y
,也就是前后的顺序仍然不变
- 讲一下拓扑排序的思路:
- 首先输入的是一个边的关系
edges[i] = [x,y]
表示,存在x->y
的有向边,我们需要将这个边,使用邻接表存储起来,同时还使用一个数组存储每一个顶点的入度数量
- 使用一个队列来存储候选将要拓展的节点,当然一开始,加入队列的元素就是
入度为0的节点
- 选取队列中的元素,加入最终的排序的数组中,同时,对于以该元素作为起始节点的边所对应的节点的入度数量应该
-1
,同时判断,将新的入度数量为0的节点加入候选队列当中 - 如果最终,排序的元素数量少于总的节点数量,则说明存在环
- 首先输入的是一个边的关系
cpp
// 返回有向无环图(DAG)的其中一个拓扑序
// 如果图中有环,返回空列表
// 节点编号从 0 到 n-1
vector<int> topologicalSort(int n, vector<vector<int>>& edges) {
vector<vector<int>> g(n);
vector<int> in_deg(n);
for (auto& e : edges) {
int x = e[0], y = e[1];
g[x].push_back(y);
in_deg[y]++; // 统计 y 的先修课数量
}
queue<int> q;
for (int i = 0; i < n; i++) {
if (in_deg[i] == 0) { // 没有先修课,可以直接上
q.push(i); // 加入学习队列
}
}
vector<int> topo_order;
while (!q.empty()) {
int x = q.front();
q.pop();
topo_order.push_back(x);
for (int y : g[x]) {
in_deg[y]--; // 修完 x 后,y 的先修课数量减一
if (in_deg[y] == 0) { // y 的先修课全部上完
q.push(y); // 加入学习队列
}
}
}
if (topo_order.size() < n) { // 图中有环
return {};
}
return topo_order;
}
拓扑排序+dp解决工程完成时间
- 拓扑排序,适合处理
有向无环图
当中,解决活动先后顺序的安排,当然,我们也可以计算出完成全部的活动所需的最少时间 - 我们定义
dp[i]
为完成任务i
的最早完成时间,那么就有递推公式dp[i] = max(dp[j) +time[i]
,其中dp[j]
为j->i
的节点
- 思路分析:
- 解决的方法,也就是在出候选队列的时候,我们在更新入度节点的时候,同时更新这个
dp[i]
即可
- 解决的方法,也就是在出候选队列的时候,我们在更新入度节点的时候,同时更新这个
cpp
class Solution {
public:
int minimumTime(int n, vector<vector<int>>& relations, vector<int>& time) {
// 拓扑排序+dp
vector<int> dp(n+1);
vector<int> ine(n+1);
vector<vector<int>> e(n+1);
queue<int> q;
for (auto p : relations){
int u = p[0],v = p[1];
e[u].push_back(v);
ine[v]++;
}
// 初始化队列
for (int i = 1; i <= n; i++){
if (ine[i] == 0){
q.push(i);
dp[i] = time[i-1];
}
}
// 开始排序
while (q.size()){
int x = q.front();
q.pop();
for (auto c : e[x]){
ine[c]--;
dp[c] = max(dp[c],dp[x] + time[c-1]);
if (ine[c] == 0){
q.push(c);
}
}
}
// 遍历完,找到这个最大的dp
int ans = 0;
for (int i = 1;i <= n; i++){
cout << dp[i] << " ";
ans = max(ans,dp[i]);
}
return ans;
}
};