C++ 图论算法:强连通分量

强连通分量 对应蓝桥云课 代码框架见下

cpp 复制代码
#include <iostream>
#include <vector>
#include <stack>
#include <cstring>
#include <algorithm>
using namespace std;

class TarjanSCC {
private:
    int n; //图的顶点数
    vector<vector<int>> adj; // 图的邻接表
    vector<int> dfn; //顶点被访问的时间戳
    vector<int> low; //顶点能够回溯到的最早时间
    vector<int> inStack; //是否在栈中
    stack<int> st; //栈
    int timeStamp; //顶点被访问的当前时间戳
    vector<int> sccId; //每个顶点属于的强连通分量编号
    int sccCount; //强连通分量个数
    vector<vector<int>> sccNodes; //每个scc包含的顶点

    void tarjanDFS(int u) {
        dfn[u] = low[u] = ++timeStamp;
        st.push(u);
        inStack[u] = true;

        for (int i = 0; i < adj[u].size(); ++i) {
            int v = adj[u][i];
            if (!dfn[v]) {
                tarjanDFS(v);
                low[u] = min(low[u], low[v]);
            }
            else if (inStack[v]) {
                low[u] = min(low[u], dfn[v]);
            }
        }
        if (dfn[u] == low[u]) {
            vector<int> scc;
            int v;

            do {
                v = st.top();
                st.pop();
                inStack[v] = false;
                sccId[v] = sccCount;
                scc.push_back(v);
            } while (v != u);
            sccNodes.push_back(scc);
            sccCount++;
        }
    }
public:
    TarjanSCC(int _n) : n(_n) {
        adj.resize(n + 1);
        dfn.resize(n + 1, 0);
        low.resize(n + 1, 0);
        inStack.resize(n + 1, false);
        sccId.resize(n + 1, 0);
        timeStamp = 0;
        sccCount = 0;
    }
    void addEdge(int u, int v) {
        adj[u].push_back(v);
    }

    void solve() {
        for (int i = 1; i <= n; ++i) {
            if (!dfn[i]) {
                tarjanDFS(i);
            }
        }
    }

    int getSCCCount() const {
        return sccCount;
    }

    int getSCCId(int u) const { return sccId[u]; }

    const vector<int>& getSCCNodes(int sccId) const {
        return sccNodes[sccId];
    }

};

int main()
{
    int n, m;
    cin >> n >> m;
    TarjanSCC scc(n);
    for (int i = 0; i < m; ++i) {
        int u, v;
        cin >> u >> v;
        scc.addEdge(u, v);
    }
    scc.solve();
    cout << scc.getSCCCount() << endl;

    vector<int> ret;
    for (int i = 0; i < scc.getSCCCount(); ++i) {
        const vector<int>& v = scc.getSCCNodes(i);
        int min = v[0];
        for (int j = 1; j < v.size(); ++j) {
            if (v[j] < min) {
                min = v[j];
            }
        }
        ret.push_back(min);
    }
    sort(ret.begin(), ret.end());
    for (int i = 0; i < ret.size(); ++i) {
        cout << ret[i] << endl;
    }
    // 请在此输入您的代码
    return 0;
}

代码练习 1 对应蓝桥云课 课表判断 代码见下

cpp 复制代码
#include <iostream>
#include <vector>
#include <stack>
#include <cstring>
#include <algorithm>
using namespace std;

class TarjanSCC {
private:
    int n; //图的顶点数
    vector<vector<int>> adj; // 图的邻接表
    vector<int> dfn; //顶点被访问的时间戳
    vector<int> low; //顶点能够回溯到的最早时间
    vector<int> inStack; //是否在栈中
    stack<int> st; //栈
    int timeStamp; //顶点被访问的当前时间戳
    vector<int> sccId; //每个顶点属于的强连通分量编号
    int sccCount; //强连通分量个数
    vector<vector<int>> sccNodes; //每个scc包含的顶点

    void tarjanDFS(int u) {
        dfn[u] = low[u] = ++timeStamp;
        st.push(u);
        inStack[u] = true;

        for (int i = 0; i < adj[u].size(); ++i) {
            int v = adj[u][i];
            if (!dfn[v]) {
                tarjanDFS(v);
                low[u] = min(low[u], low[v]);
            }
            else if (inStack[v]) {
                low[u] = min(low[u], dfn[v]);
            }
        }
        if (dfn[u] == low[u]) {
            vector<int> scc;
            int v;

            do {
                v = st.top();
                st.pop();
                inStack[v] = false;
                sccId[v] = sccCount;
                scc.push_back(v);
            } while (v != u);
            sccNodes.push_back(scc);
            sccCount++;
        }
    }
public:
    TarjanSCC(int _n) : n(_n) {
        adj.resize(n + 1);
        dfn.resize(n + 1, 0);
        low.resize(n + 1, 0);
        inStack.resize(n + 1, false);
        sccId.resize(n + 1, 0);
        timeStamp = 0;
        sccCount = 0;
    }
    void addEdge(int u, int v) {
        adj[u].push_back(v);
    }

    void solve() {
        for (int i = 1; i <= n; ++i) {
            if (!dfn[i]) {
                tarjanDFS(i);
            }
        }
    }

    int getSCCCount() const {
        return sccCount;
    }

    int getSCCId(int u) const { return sccId[u]; }

    const vector<int>& getSCCNodes(int sccId) const {
        return sccNodes[sccId];
    }

};

int main()
{
    int t;
    cin >> t;
    while(t--){
      int n, m;
      cin >> n >> m;
      TarjanSCC scc(n);
      for(int i=0; i<m; ++i){
        int u, v;
        cin >> u >> v;
        scc.addEdge(u, v);
      }
      scc.solve();
      if(scc.getSCCCount() == n){
        cout << "YES" << endl;
      }else{
        cout << "NO" << endl;
      }
    }
    // 请在此输入您的代码
    return 0;
}

代码练习2 对应蓝桥云课 互可达点对数 代码见下

cpp 复制代码
#include <iostream>
#include <vector>
#include <stack>
#include <cstring>
#include <algorithm>
using namespace std;

class TarjanSCC {
private:
    int n; //图的顶点数
    vector<vector<int>> adj; // 图的邻接表
    vector<int> dfn; //顶点被访问的时间戳
    vector<int> low; //顶点能够回溯到的最早时间
    vector<int> inStack; //是否在栈中
    stack<int> st; //栈
    int timeStamp; //顶点被访问的当前时间戳
    vector<int> sccId; //每个顶点属于的强连通分量编号
    int sccCount; //强连通分量个数
    vector<vector<int>> sccNodes; //每个scc包含的顶点

    void tarjanDFS(int u) {
        dfn[u] = low[u] = ++timeStamp;
        st.push(u);
        inStack[u] = true;

        for (int i = 0; i < adj[u].size(); ++i) {
            int v = adj[u][i];
            if (!dfn[v]) {
                tarjanDFS(v);
                low[u] = min(low[u], low[v]);
            }
            else if (inStack[v]) {
                low[u] = min(low[u], dfn[v]);
            }
        }
        if (dfn[u] == low[u]) {
            vector<int> scc;
            int v;

            do {
                v = st.top();
                st.pop();
                inStack[v] = false;
                sccId[v] = sccCount;
                scc.push_back(v);
            } while (v != u);
            sccNodes.push_back(scc);
            sccCount++;
        }
    }
public:
    TarjanSCC(int _n) : n(_n) {
        adj.resize(n + 1);
        dfn.resize(n + 1, 0);
        low.resize(n + 1, 0);
        inStack.resize(n + 1, false);
        sccId.resize(n + 1, 0);
        timeStamp = 0;
        sccCount = 0;
    }
    void addEdge(int u, int v) {
        adj[u].push_back(v);
    }

    void solve() {
        for (int i = 1; i <= n; ++i) {
            if (!dfn[i]) {
                tarjanDFS(i);
            }
        }
    }

    int getSCCCount() const {
        return sccCount;
    }

    int getSCCId(int u) const { return sccId[u]; }

    const vector<int>& getSCCNodes(int sccId) const {
        return sccNodes[sccId];
    }

};

int main()
{
    int t;
    cin >> t;
    while(t--){
      int n,m;
      cin >> n >> m;
      TarjanSCC scc(n);
      for(int i=0; i<m; ++i){
        int u, v;
        cin >> u >> v;
        scc.addEdge(u, v);
      }
      scc.solve();
      int ans = 0;
      for(int i=0; i<scc.getSCCCount(); ++i){
        int size = scc.getSCCNodes(i).size();
        ans += size * (size - 1) / 2;
      }
      cout << ans << endl;

    }
    // 请在此输入您的代码
    return 0;
}

代码练习 3 最小强连通边数 对应蓝桥云课 代码见下

cpp 复制代码
#include <iostream>
#include <vector>
#include <stack>
#include <cstring>
#include <algorithm>
#include <unordered_set>
using namespace std;

class TarjanSCC {
private:
    int n; //图的顶点数
    vector<vector<int>> adj; // 图的邻接表
    vector<int> dfn; //顶点被访问的时间戳
    vector<int> low; //顶点能够回溯到的最早时间
    vector<int> inStack; //是否在栈中
    stack<int> st; //栈
    int timeStamp; //顶点被访问的当前时间戳
    vector<int> sccId; //每个顶点属于的强连通分量编号
    int sccCount; //强连通分量个数
    vector<vector<int>> sccNodes; //每个scc包含的顶点

    void tarjanDFS(int u) {
        dfn[u] = low[u] = ++timeStamp;
        st.push(u);
        inStack[u] = true;

        for (int i = 0; i < adj[u].size(); ++i) {
            int v = adj[u][i];
            if (!dfn[v]) {
                tarjanDFS(v);
                low[u] = min(low[u], low[v]);
            }
            else if (inStack[v]) {
                low[u] = min(low[u], dfn[v]);
            }
        }
        if (dfn[u] == low[u]) {
            vector<int> scc;
            int v;

            do {
                v = st.top();
                st.pop();
                inStack[v] = false;
                sccId[v] = sccCount;
                scc.push_back(v);
            } while (v != u);
            sccNodes.push_back(scc);
            sccCount++;
        }
    }
public:
    TarjanSCC(int _n) : n(_n) {
        adj.resize(n + 1);
        dfn.resize(n + 1, 0);
        low.resize(n + 1, 0);
        inStack.resize(n + 1, false);
        sccId.resize(n + 1, 0);
        timeStamp = 0;
        sccCount = 0;
    }
    void addEdge(int u, int v) {
        adj[u].push_back(v);
    }

    void solve() {
        for (int i = 1; i <= n; ++i) {
            if (!dfn[i]) {
                tarjanDFS(i);
            }
        }
    }

    int getSCCCount() const {
        return sccCount;
    }

    int getSCCId(int u) const { return sccId[u]; }

    const vector<int>& getSCCNodes(int sccId) const {
        return sccNodes[sccId];
    }

    const vector<int>& getEdges(int u) const{
      return adj[u];
    }

};

class ShrinkGraph{
private:
  int n;
  vector<unordered_set<int>> adj;
  vector<int> ind, outd;

  void init(int sccCount){
    n = sccCount;
    ind.resize(n, 0);
    outd.resize(n, 0);
    adj.resize(n, {});
  } 

  void addEdge(int u, int v){
    if(u == v){
      return ;
    }
    if(adj[u].find(v) != adj[u].end()){
      return ;
    }
    adj[u].insert(v);
    outd[u]++;
    ind[v]++;

  }
public:
  void shrinkFromSCC(const TarjanSCC& scc){
    init(scc.getSCCCount());
    for(int i=0; i<n; ++i){
      const vector<int>& v = scc.getSCCNodes(i);
      for(int j=0; j<v.size(); ++j){
        const vector<int>& org = scc.getEdges(v[j]);
        for(int k=0; k<org.size(); ++k){
          int u = scc.getSCCId(v[j]);
          int v = scc.getSCCId(org[k]);
          addEdge(u, v);
        }
      } 
    }
  }
  int solve(){
    int indCount = 0, outdCount = 0;
    for(int i=0; i<n; ++i){
      if(!ind[i]) indCount++;
      if(!outd[i]) outdCount++;
    }
    return max(indCount, outdCount);
  }
};

int main()
{
    int t;
    cin >> t;
    while(t--){
      int n, m;
      cin >> n >> m;
      TarjanSCC scc(n);
      for(int i=0; i<m; ++i){
        int u, v;
        cin >> u >> v;
        scc.addEdge(u ,v);
      }
      scc.solve();
      ShrinkGraph sg;
      sg.shrinkFromSCC(scc);
      cout << sg.solve() << endl;

    }
    // 请在此输入您的代码
    return 0;
}
相关推荐
一起养小猫3 小时前
LeetCode100天Day13-移除元素与多数元素
java·算法·leetcode
a***59263 小时前
C++跨平台开发:挑战与解决方案
开发语言·c++
ACERT3333 小时前
10.吴恩达机器学习——无监督学习01聚类与异常检测算法
python·算法·机器学习
诗词在线3 小时前
从算法重构到场景复用:古诗词数字化的技术破局与落地实践
python·算法·重构
不穿格子的程序员3 小时前
从零开始写算法——二叉树篇7:从前序与中序遍历序列构造二叉树 + 二叉树的最近公共祖先
数据结构·算法
hetao17338373 小时前
2026-01-12~01-13 hetao1733837 的刷题笔记
c++·笔记·算法
Yu_Lijing3 小时前
基于C++的《Head First设计模式》笔记——外观模式
c++·笔记·设计模式
CoderCodingNo3 小时前
【GESP】C++六级考试大纲知识点梳理, (5) 动态规划与背包问题
开发语言·c++·动态规划
情缘晓梦.3 小时前
C++ 类和对象(完)
开发语言·jvm·c++
无限码力3 小时前
美团秋招笔试真题 - 放它一马 & 信号模拟
算法·美团秋招·美团笔试·美团笔试真题