tarjan算法

求强连通分量:

#include <bits/stdc++.h>

using namespace std;

int main() {
  int n, m;
  scanf("%d%d", &n, &m);

  vector<vector<int>> adj(n + 1);
  for (int i = 0; i < m; i++) {
    int u, v;
    scanf("%d%d", &u, &v);
    adj[u].push_back(v);
  }

  vector<int> dfn(n + 1);  // dfs 森林每个点的时间戳
  vector<int> low(n + 1);  // dfs 森林每个点能跳到的所用点中最小的时间戳
  vector<bool> is_in_stack(n + 1);  // 判断每个点是否在栈中
  vector<int> belong(n + 1);  // 记录每个点属于哪个强连通分量中
  stack<int> stk;  // 存储可能在强连通分量中的点
  int timestamp = 0;  // dfs 时的时间戳
  vector<vector<int>> scc;  // 记录强连通分量
  int cnt = 0;  // 强连通分量的数量

  function <void(int)> dfs = [&](int u) {
    dfn[u] = low[u] = ++timestamp;
    is_in_stack[u] = true;
    stk.push(u);

    for (auto v : adj[u]) {
      if (!dfn[v]) {
        dfs(v);
        low[u] = min(low[u], low[v]);
      } else {
        if (is_in_stack[v]) {
          low[u] = min(low[u], dfn[v]);
        }
      }
    }

    if (dfn[u] == low[u]) {
      ++cnt;
      vector<int> tmp;
      while (true) {
        int v = stk.top();
        tmp.push_back(v);
        belong[v] = cnt;
        is_in_stack[v] = false;
        stk.pop();
        if (v == u) {
          break;
        }
      }
      // 如果需要对强连通分量里的元素排序加此行
      sort(tmp.begin(), tmp.end());
      scc.push_back(std::move(tmp));
    }
  };

  for (int i = 1; i <= n; i++) {
    if (!dfn[i]) {
      dfs(i);
    }
  }
  sort(scc.begin(), scc.end());

  for (auto c : scc) {
    for (auto u : c) {
      printf("%d ", u);
    }
    printf("\n");
  }
}

/*
5 6
1 3
3 1
2 4
4 5
5 2
1 2

ans:
1 3
2 4 5
*/

缩点

题目链接:缩点

#include <bits/stdc++.h>

using namespace std;

int main() {
  int n, m;
  scanf("%d%d", &n, &m);
  
  vector<int> w(n + 1);
  for (int i = 1; i <= n; i++) {
    scanf("%d", &w[i]);
  }

  vector<vector<int>> adj(n + 1);
  for (int i = 0; i < m; i++) {
    int u, v;
    scanf("%d%d", &u, &v);
    adj[u].push_back(v);
  }

  vector<int> dfn(n + 1);  // dfs 森林每个点的时间戳
  vector<int> low(n + 1);  // dfs 森林每个点能跳到的所用点中最小的时间戳
  vector<bool> is_in_stack(n + 1);  // 判断每个点是否在栈中
  vector<int> belong(n + 1);  // 记录每个点属于哪个强连通分量中
  stack<int> stk;  // 存储可能在强连通分量中的点
  int timestamp = 0;  // dfs 时的时间戳
  int cnt = 0;  // 强连通分量的数量

  function <void(int)> dfs = [&](int u) {
    dfn[u] = low[u] = ++timestamp;
    is_in_stack[u] = true;
    stk.push(u);

    for (auto v : adj[u]) {
      if (!dfn[v]) {
        dfs(v);
        low[u] = min(low[u], low[v]);
      } else {
        if (is_in_stack[v]) {
          low[u] = min(low[u], dfn[v]);
        }
      }
    }

    if (dfn[u] == low[u]) {
      ++cnt;
      while (true) {
        int v = stk.top();
        belong[v] = cnt;
        
        is_in_stack[v] = false;
        stk.pop();
        if (v == u) {
          break;
        }
      }
    }
  };

  for (int i = 1; i <= n; i++) {
    if (!dfn[i]) {
      dfs(i);
    }
  }
  
  vector<int> val(cnt + 1);
  for (int i = 1; i <= n; i++) {
    val[belong[i]] += w[i];
  }
  
  // 只有一个强连通分量,直接返回
  if (cnt == 1) {
    printf("%d\n", val[1]);
    return 0;
  }

  // 对强连通分量建图
  vector<vector<int>> e(cnt + 1);
  // 防止重复计算入度
  map<pair<int, int>, bool> mp;
  vector<int> in_degree(n + 1);  // 每个强连通分量的入度
  for (int i = 1; i <= n; i++) {
    for (auto v : adj[i]) {
      if (belong[i] != belong[v] && mp[{belong[i], belong[v]}] == false) {
        in_degree[belong[v]]++;
        e[belong[i]].push_back(belong[v]);
        mp[{belong[i], belong[v]}] = true;
      }
    }
  }
  
  vector<int> dp(cnt + 1);

  auto topsort = [&]() {
    queue<int> q;
    for (int i = 1; i <= cnt; i++) {
      if (in_degree[i] == 0) {
        q.push(i);
      }
    }

    while (q.size()) {
      int u = q.front();
      q.pop();
      // 遍历到路径上的最后一个点,需要加上这个点的权值
      if (e[u].size() == 0) {
        dp[u] += val[u];
      }

      for (auto v : e[u]) {
        in_degree[v]--;
        dp[v] = max(dp[v], dp[u] + val[u]);
        if (in_degree[v] == 0) {
          q.push(v);
        }
      }
    }
  };

  topsort();
  
  int ans = *max_element(dp.begin(), dp.end());
  
  printf("%d\n", ans);
}
posted @   hacker_dvd  阅读(4)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
点击右上角即可分享
微信分享提示