DAG拓扑排序

1|0DAG拓扑排序

1|1引入

小学奥数类型题。

沏茶过程

(烧水壶) 到 (接水) 到 (烧水 洗茶杯 找茶叶)(并行) 到 (沏茶)

即有先后顺序的流程,且必须所有步骤都能执行。

1|2概述

  • 拓扑排序是对DAG(有向无环图)的顶点进行的一种线性排序,排序序列中每个顶点都会且仅会出现一次,且对于所有有向边 uv,排序完后 u 都在 v 的前面。
  • 如果图中存在环,就不能进行拓扑排序。
  • 一个有向无环图可能有多种排序结果。
    • 对于 n 个顶点的有向无环图:
      • 最少存在 1 种合法的拓扑序列。
        • 即该图为一条直线。
      • 最多存在 n! 钟合法的拓扑序列。
        • 即该图为散点,无边。

1|3流程

  • 在拓扑排序中,用 L 记录到目前为止的拓扑序列,用集合 S 记录所有不在 L 中入度为 0 的顶点。
  • 步骤执行
    • 首先遍历整张图上的顶点,如果一个顶点入度为 0,将它加入 S
    • S 不为空时:
      • S 中任取一个顶点 x,将 x 加入到 L 的队尾,并把 xS 中删去。
      • 遍历从 x 出发的边 xy,删除。如果 y 的入度变为 0,则将其加入 S 中。
    • 循环结束时:
      • 如果所有点都加入了 L,那么我们就找到了一个合法的拓扑序列。
      • 如果有点不在 L 中,证明该图有环。
  • L,S 用同一个队列维护。
  • 时间复杂度 O(n+m)

1|4代码

  • STL

int main()
{
cin >> n >> m;
for (int _ = 0, x, y; _ < m; _++)
{
cin >> x >> y;
add_edge(x, y);
d[y]++;
}

int cnt = 0; queue<int> q; for (int i = 1; i <= n; i++) { if (d[i] == 0) q.push(i), cnt++;//先加入入度为0的点 } while(!q.empty()) {//拓扑排序 int u = q.front(); q.pop(); for (int i = head[u]; i; i = edge[i].next) { int to = edge[i].to; d[to]--; if (d[to] == 0) q.push(to), cnt++; } } if (cnt == n) YES;//如果n个点都排序成功了,说明无环 else NO;

}
```

  • 手写
vector<int> edge[N + 1]; int n, m, q[N + 1], d[N + 1]//q为队列,d为入度; void topoSort() { int front = 1, rear = 0; for (int i = 1; i <= n; i++) { if (!d[i]) q[++rear] = i; while(front <= rear) { int x = q[front]; ++front; for (auto to : edge[x]) { if (--d[to] == 0)//没必要真的删边,维护d数组即可 { q[++rear] = to; } } } } if (rear == n) { printf("合法"); } else { printf("不合法"); } }

1|5例题

  • P1113 杂务

    • 利用拓扑排序思想来记忆化 dfs
    #include<iostream> #include<algorithm> #include<cstdio> #include<vector> const int N = 1e4 + 10; std::vector<int> m[N]; int vis[N]; int n; int ans; int len[N]; int u, v; int dfs(int x) { if (vis[x]) return vis[x]; for (auto to : m[x]) vis[x] = std::max(vis[x], dfs(to)); vis[x] += len[x]; return vis[x]; } int main() { std::ios::sync_with_stdio(false); std::cin.tie(nullptr); std::cout.tie(nullptr); std::cin >> n; for (int i = 1; i <= n; i++) { std::cin >> u >> len[i]; while(std::cin >> v && v != 0) m[v].push_back(u); } for (int i = 1; i <= n; i++) ans = std::max(ans, dfs(i)); std::cout << ans << std::endl; return 0; }
  • P4017 最大食物链计数

    • 体会拓扑排序的作用
  • P1983 NOIP2013 普及组 车站分级

    • 建构模型,形成图之后再拓扑。
  • P1347 排序

    • 对拓扑排序判断无解,判断最长拓扑链长度的方式的考察

__EOF__

本文作者Kdlyh
本文链接https://www.cnblogs.com/kdlyh/p/17864601.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   加固文明幻景  阅读(22)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示