tarjan算法模板(强连通分量)

int low[10010],dfn[10010],time;   //time时间戳  low记录顶点能到达最早的时间戳 dfn记录顶点时间戳
bool instack[10010];    //判断是否在栈内 
int count;              //强连通分量数 
int belong[10010];      // 记录顶点所属的强连通分量 
int in[10010];         //记录入度 
int size[10010];        //记录每个强连通分量的顶点数 
stack<int>st;
void tarjan(int s)
{
    dfn[s]=low[s]=++time;
    st.push(s);
    instack[s]=true;
    for(int i=0;i<e[s].size();i++)
    {
        int v=e[s][i];
        if(dfn[v]==-1)
        {
            tarjan(v);
            low[s]=min(low[v],low[s]);
        }
        else if(instack[v])
            low[s]=min(dfn[v],low[s]);
    }    
    if(dfn[s]==low[s])
    {
        int u;
        count++;
        do
        {
            u=st.top();
            st.pop();
            instack[u]=false;
            belong[u]=count;
            size[count]++;
        }while(u!=s);    
    }
}

### 强连通分量缩点算法模板 强连通分量(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]。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值