求强连通分量 - 模板

求的是最大强连通分量且字典序最小

#include <bits/stdc++.h>

using namespace std;

#define
INF 0x3f3f3f3f #define MAXN 100010 #define MAXM 5010 inline int read() { int x = 0,ff = 1;char ch = getchar(); while(!isdigit(ch)) { if(ch == '-') ff = -1; ch = getchar(); } while(isdigit(ch)) { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar(); } return x * ff; } int n,m,k,ti = 0,cnt = 0,dfn[MAXN],low[MAXN],cut[MAXN]; int lin[MAXN],tot = 1,vis[MAXN],id[MAXN],len[MAXN]; stack < int > scc; struct edge { int y,next; }e[MAXN]; inline void add(int xx,int yy) { e[++tot].y = yy; e[tot].next = lin[xx]; lin[xx] = tot; } void Tarjan(int x) { dfn[x] = low[x] = ++ti; scc.push(x); vis[x] = 1; for(int i = lin[x],y;i;i = e[i].next) { if(!dfn[y = e[i].y]) { Tarjan(y); low[x] = min(low[x],low[y]); } else if(vis[y]) low[x] = min(low[x],dfn[y]); } if(dfn[x] == low[x]) { cnt++; int y; do { y = scc.top(); scc.pop(); vis[y] = false; id[y] = cnt; //点y属于哪一个连通分量 ++len[cnt]; //该连通分量的长度 } while (x != y); } } int main() { n = read(); m = read(); for(int i = 1;i <= m;++i) { int x,y; x = read(); y = read(); add(x,y); //单向图 } for(int i = 1;i <= n;++i) if(!dfn[i]) Tarjan(i); int l = 0; for(int i = 1;i <= cnt;++i) { if(l < len[id[i]]) { k = i; l = len[id[i]]; } } printf("%d\n",l); for(int i = 1;i <= n;++i) { if(id[i] == id[k]) printf("%d ",i); } return 0; }

 

转载于:https://www.cnblogs.com/AK-ls/p/10389087.html

### 强连通分量缩点算法模板 强连通分量(Strongly Connected Component, SCC)是指在一个有向图中,任意两个节点都可以互相到达的最大子图。通过 Tarjan 算法可以高效地找到这些强连通分量并将其缩成单个节点,从而简化原图结构。 以下是基于 Tarjan 算法的 C++ 实现代码模板: ```cpp #include <bits/stdc++.h> using namespace std; const int MAXN = 1e5 + 5; // 节点数量上限 int n, m; vector<int> adj[MAXN]; // 邻接表存储图 stack<int> stk; // 辅助栈 bool in_stack[MAXN]; // 判断当前节点是否在栈中 int dfn[MAXN], low[MAXN]; // 时间戳数组和追溯值数组 int scc_id[MAXN]; // 存储每个节点所属的强连通分量编号 int idx = 0, cnt_scc = 0; // 当前时间戳计数器 和 强连通分量计数器 void tarjan(int u) { dfn[u] = low[u] = ++idx; // 初始化dfn和low stk.push(u); // 将u压入栈 in_stack[u] = true; // 标记u已在栈中 for (auto &v : adj[u]) { // 枚举所有邻接点 if (!dfn[v]) { // 如果v未访问过 tarjan(v); low[u] = min(low[u], low[v]); // 更新low[u] } else if (in_stack[v]) { // 如果v已经在栈中 low[u] = min(low[u], dfn[v]); } } if (dfn[u] == low[u]) { // 找到一个新的强连通分量 ++cnt_scc; // 新增一个SCC编号 while (true) { // 提取该SCC中的所有节点 int v = stk.top(); stk.pop(); in_stack[v] = false; scc_id[v] = cnt_scc; // 记录属于哪个SCC if (v == u) break; // 当弹出的是u时结束循环 } } } // 主函数调用部分 void solve() { cin >> n >> m; // 输入节点数和边数 for (int i = 1; i <= m; ++i) { int u, v; cin >> u >> v; // 输入一条有向边 adj[u].push_back(v); } memset(dfn, 0, sizeof(dfn)); // 初始化dfn数组 memset(in_stack, 0, sizeof(in_stack)); idx = cnt_scc = 0; for (int i = 1; i <= n; ++i) { // 对每个节点运行tarjan if (!dfn[i]) tarjan(i); } cout << "Total Strongly Connected Components: " << cnt_scc << "\n"; } ``` #### 关键说明 上述代码实现了 Tarjan 算法的核心逻辑[^2]。 - `dfn` 数组记录了每个节点第一次被访问的时间戳。 - `low` 数组用于追踪能够回溯到的最早祖先节点的时间戳。 - 使用辅助栈来保存当前路径上的节点,并判断哪些节点已经形成一个完整的强连通分量- 每次当发现某个节点满足条件 `dfn[u] == low[u]` 时,表示找到了一个新的强连通分量[^3]。 完成 Tarjan 算法后,可以通过构建新的缩点后的图来进行进一步处理。具体做法如下: - 创建新图,其中每个节点代表原始图的一个强连通分量- 统计每条边连接的不同强连通分量之间的关系,忽略内部重复边。 --- ### 缩点后的应用实例 假设我们需要计算拓扑序或者解决某些动态规划问题,在缩点之后的新图上操作会更加简单有效。例如,对于 DAG 上的经典 DP 或最短路问题,可以直接在此基础上展开分析。 ```cpp vector<vector<int>> new_adj(cnt_scc + 1); // 缩点后的新图 long long indegree[cnt_scc + 1] = {}; // 入度统计 for (int u = 1; u <= n; ++u) { for (auto &v : adj[u]) { if (scc_id[u] != scc_id[v]) { // 只考虑跨不同SCC的边 new_adj[scc_id[u]].push_back(scc_id[v]); indegree[scc_id[v]]++; } } } ``` 此段代码展示了如何根据原有图生成缩点后的图结构[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值