拓扑排序

最近愈发觉得自己 acm 要寄,没心情学新东西,就稍微水了点拓扑排序中的简单题,记录一下题解

拓扑排序简述

定义

拓扑排序要解决的问题是给一个图的所有节点排序。

我们可以拿大学选课的例子来描述这个过程,比如学习大学课程中有:单变量微积分,线性代数,离散数学概述,概率论与统计学概述,语言基础,算法导论,机器学习。当我们想要学习 算法导论 的时候,就必须先学会 离散数学概述 和 概率论与统计学概述,不然在课堂就会听的一脸懵逼。当然还有一个更加前的课程 单变量微积分。这些课程就相当于几个顶点 u, 顶点之间的有向边 (u,v) 就相当于学习课程的顺序。显然拓扑排序不是那么的麻烦,不然你是如何选出合适的学习顺序。下面将介绍如何将这个过程抽象出来,用算法来实现。

但是如果某一天排课的老师打瞌睡了,说想要学习 算法导论,还得先学 机器学习,而 机器学习 的前置课程又是 算法导论,然后你就一万脸懵逼了,我到底应该先学哪一个?当然我们在这里不考虑什么同时学几个课程的情况。在这里,算法导论 和 机器学习 间就出现了一个环,显然你现在没办法弄清楚你需要学什么了,于是你也没办法进行拓扑排序了。因而如果有向图中存在环路,那么我们就没办法进行 拓扑排序 了。

因此我们可以说 在一个 DAG(有向无环图) 中,我们将图中的顶点以线性方式进行排序,使得对于任何的顶点 u 到 v 的有向边 (u,v), 都可以有 u 在 v 的前面。

还有给定一个 DAG,如果从 i 到 j 有边,则认为 j 依赖于i 。如果 i 到 j 有路径(i 可达 j),则称 j 间接依赖于 i。

拓扑排序的目标是将所有节点排序,使得排在前面的节点不能依赖于排在后面的节点。

应用

一、P6145 [USACO20FEB]Timeline G

传送门:P6145 [USACO20FEB]Timeline G

对于每个三元组 (a,b,x),从 a 向 b 连一条长度为 x 的边,同时新建一个超级源点 0,从 0 向 i 连长度为 S[i] 的边。容易发现这张图是一个有向无环图,拓扑排序递推即可

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;

struct node{
  int v, w, nxt;
}e[maxn];
int n, m, c, cnt;
int s[maxn], indu[maxn], head[maxn];
queue<int> q;

inline void add(int u, int v, int w) {
  e[++cnt].v = v;
  e[cnt].w = w;
  e[cnt].nxt = head[u];
  head[u] = cnt;
}

int main() {
  ios::sync_with_stdio(false);
  cin.tie(0); cout.tie(0);
  cin >> n >> m >> c;
  for (int i = 1; i <= n; i++) cin >> s[i];
  for (int i = 1; i <= c; i++) {
    register int u, v, w;
    cin >> u >> v >> w;
    add(u, v, w); 
    indu[v]++;
  }
  for (int i = 1; i <= n; i++)
    if (indu[i] == 0) q.push(i);
  while (!q.empty()) {
    int u = q.front(); q.pop();
    for (int i = head[u]; i; i = e[i].nxt) {
      int v = e[i].v, w = e[i].w;
      s[v] = max(s[v], s[u] + w);
      indu[v]--;
      if (indu[v] == 0) q.push(v);
    }
  }
  for (int i = 1; i <= n; i++) cout << s[i] << endl;
  return 0;
}

二、P4316 绿豆蛙的归宿

传送门:P4316 绿豆蛙的归宿

设 f[x] 表示 x 点到终点的期望路径总长度,显然有 f[n] = 0,那么对于一条 x->y 的有向边,有 f[x]=sigma(f[y]+w[i])/degree[x],其中 degree[x] 表示 x 点的出度,w[i] 表示这条边的边权,那么假设我们已经知道了 f[y],就可以反推 f[x],所以只需要反向建边之后跑个拓扑排序就行了,那么最后答案即为 f[1]

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;

struct node{
  int v, w, nxt;
}e[maxn * 3];
int n, m, cnt;
int head[maxn], indu[maxn], num[maxn];
double dp[maxn];
queue<int> q;

inline void add(int u, int v, int w) {
  e[++cnt].v = v;
  e[cnt].w = w;
  e[cnt].nxt = head[u];
  head[u] = cnt;
}

int main() {
  ios::sync_with_stdio(false);
  cin.tie(0); cout.tie(0);
  cin >> n >> m;
  for (int i = 1; i <= m; i++) {
    register int u, v, w;
    cin >> u >> v >> w;
    add(v, u, w);
    indu[u]++; num[u]++;
  }
  q.push(n); dp[n] = 0;
  while (!q.empty()) {
    int u = q.front(); q.pop();
    for (int i = head[u]; i; i = e[i].nxt) {
      int v = e[i].v, w = e[i].w;
      dp[v] += (dp[u] + w) / num[v];
      indu[v]--;
      if (indu[v] == 0) q.push(v);
    }
  }
  printf("%.2lf", dp[1]);
  return 0;
}

三、P1038 [NOIP2003 提高组] 神经网络

需要注意的一点是初始层的阈值不可以减去

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e4 + 10;

struct node{
  int v, w, nxt;
}e[maxn * 3];
int n, m, u, cnt;
int head[maxn], vis[maxn], c[maxn], num[maxn];
queue<int> q;

inline void add(int u, int v, int w) {
  e[++cnt].v = v;
  e[cnt].w = w;
  e[cnt].nxt = head[u];
  head[u] = cnt;
}

int main() {
  ios::sync_with_stdio(false);
  cin.tie(0); cout.tie(0);
  cin >> n >> m;
  for (int i = 1; i <= n; i++) {
    cin >> c[i] >> u;
    if (c[i] > 0) {
      q.push(i);
      vis[i] = 1;
    } else c[i] -= u;
  }
  for (int i = 1; i <= m; i++) {
    register int u, v, w;
    cin >> u >> v >> w;
    add(u, v, w);
    num[u] = 1;
  }
  while (!q.empty()) {
    int u = q.front(); q.pop();
    if (c[u] < 0) continue;
    for (int i = head[u]; i; i = e[i].nxt) {
      int v = e[i].v, w = e[i].w;
      c[v] += c[u] * w;
      if (vis[v] == 0) {
        q.push(v);
  	vis[v] = 1;
      }
    }
  }
  bool flag = false;
  for (int i = 1; i <= n; i++) 
    if (num[i] == 0 && c[i] > 0) {
      cout << i << " " << c[i] << endl;
      flag = true;
    }
  if (!flag) cout << "NULL" << endl;
  return 0;
}
posted @ 2022-01-18 16:13  Moominn  阅读(46)  评论(0编辑  收藏  举报