图论--tarjan缩点

本文详细介绍Tarjan算法原理及实现过程,通过洛谷P3387题目演示如何使用Tarjan算法进行强连通分量分析,并进一步利用动态规划求解最大值问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

模板:洛谷P3387
关于tarjan大神的算法我也只能抄抄代码了(滑稽)
其实《算法竞赛入门经典:训练指南》里有讲解,很详细。
代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
vector<int> G[10001];
int n,m;
int dfn_clock;
int v[10001];
int head[10001];
int dfn[10001];
int low[10001];
int vis[10001];
stack<int> s;
int sccnum[10001];
int scccnt;
vector<int> S[10001];
int V[10001];
int in[10001];
void tarjan(int x){
    dfn[x]=low[x]=++dfn_clock;
    vis[x]=1;
    in[x]=1;
    s.push(x);
    for(int i=0;i<G[x].size();i++){
        int u=G[x][i];
        if(!vis[u]){
            tarjan(u);
            low[x]=min(low[x],low[u]);
        }
        else if(in[u]){
            low[x]=min(low[x],dfn[u]);
        }
    }
    if(dfn[x]==low[x]){
        scccnt++;
        while(s.top()!=x){
            sccnum[s.top()]=scccnt;
            V[scccnt]+=v[s.top()];
            in[s.top()]=0;
            s.pop();
        }
        sccnum[x]=scccnt;
        V[scccnt]+=v[x];
        in[x]=0;
        s.pop();
    }
}
int f[10001];
int dp(int x){
    if(f[x]){
        return f[x];
    }
    f[x]=V[x];
    int mx=0;
    for(int i=0;i<S[x].size();i++){
        int u=S[x][i];
        mx=max(mx,dp(u));
    }
    f[x]+=mx;
    return f[x];
}
int main(){
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&v[i]);
    }
    for(int i=1;i<=m;i++){
        int x,y;
        scanf("%d %d",&x,&y);
        G[x].push_back(y);
    }
    for(int i=1;i<=n;i++){
        if(!vis[i]){
            tarjan(i);
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=0;j<G[i].size();j++){
            int u=G[i][j];
            if(sccnum[i]!=sccnum[u]){
                S[sccnum[i]].push_back(sccnum[u]);
            }
        }
    }
    int ans=0;
    for(int i=1;i<=scccnt;i++){
        if(!f[i]){
            ans=max(ans,dp(i));
        }
    }
    printf("%d",ans);
    return 0;
}
<think>好的,我现在需要解释图论中的算法。首先,我得回忆一下的基本概念和相关算法。通常和强连通分量有关,对吧?记得Tarjan算法可以用来寻找强连通分量,然后将每个强连通分量成一个,从而简化图的结构。 用户提到的引用[1]和[2]都提到了Tarjan算法和的应用。比如,引用[1]指出Tarjan算法能处理强连通分量等问题,而引用[2]中的代码示例展示了如何将原图的边连接后的图。这说明后的图是通过将同一强连通分量内的合并,并在不同分量之间建立新的边。 接下来,我需要详细说明的步骤。首先,用Tarjan算法找到所有强连通分量,这一步是关键。然后,为每个分量分配一个代表节,通常是分量中的某个。之后,遍历原图的边,如果边的两端属于不同分量,就在后的图中添加对应的边,但要避免重复。 要注意的是,后的图是有向无环图(DAG),这有助于后续处理,比如拓扑排序或动态规划。用户可能想知道的应用场景,比如在解决必经或路径问题时,将图转换为DAG可以简化计算。 另外,需要确保解释清晰,符合用户的要求:使用中文、正确LaTeX格式,比如提到强连通分量时用$SCC$,独立公式用$$...$$。可能还需要生成相关问题,比如的应用、Tarjan算法的步骤,或者后的图性质等。 现在要检查是否有遗漏,比如的目的和好处,以及具体例子。例如,引用[2]中的代码展示了如何连接不同分量的,这部分可以作为示例说明。同时,要确保引用标注正确,比如在提到Tarjan算法时添加[^1],在构建新图时引用[^2]。 最后,生成的相关问题需要围绕的延伸,比如算法步骤、复杂度、应用实例等。用户可能还想知道与其他图论算法的结合使用,比如拓扑排序后的操作。</think>### 图论中的算法解释 ****是图论中处理强连通分量(Strongly Connected Component, $SCC$)的核心技术,通过将每个$SCC$合并为单个节,将原图简化为有向无环图(DAG),从而简化问题复杂度。 #### 算法步骤 1. **寻找强连通分量** 使用**Tarjan算法**或**Kosaraju算法**识别图中的所有$SCC$。以Tarjan算法为例,其核心是通过深度优先搜索(DFS)和栈维护,判断节的$low$值与$dfn$值是否相等来确定$SCC$边界[^1]。 $$low(u) = \min \{ dfn(u), \low(v) \mid (u,v) \in E, v \text{在栈中}, dfn(v) < dfn(u) \}$$ 2. **构建后的图** - 为每个$SCC$分配唯一标识(如分量编号)。 - 遍历原图的所有边$(u, v)$,若$u$和$v$属于不同$SCC$,则在后的图中添加对应的边(需去重)。 #### 示例代码(后的边连接) 引用[2]中的代码片段展示了如何连接后的图: ```cpp for (int i = 1; i <= m; ++i) { int a = d[way[i].from]; // 起所属的SCC编号 int b = d[way[i].to]; // 终所属的SCC编号 if (a != b) // 不属于同一分量 g[a].push_back(b); // 添加边 } ``` #### 应用场景 - **简化问题**:将原图转换为DAG后,可结合拓扑排序解决路径问题(如最长路径、关键路径)。 - **强连通性分析**:识别网络中的紧密关联子图(如社交网络中的社群)。 - **动态规划优化**:在DAG上直接进行状态转移,避免环的影响。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值