首页 > 编程语言 >Tarjan 算法(超详细!!)

Tarjan 算法(超详细!!)

时间:2024-01-24 22:23:20浏览次数:32  
标签:Tarjan int top 算法 dfn MAXN low 详细 ans

Tarjan 算法

前言

说来惭愧,这个模板仅是绿的算法至今我才学会。

我还记得去年 CSP2023 坐大巴路上拿着书背 Tarjan 的模板。虽然那年没有考连通分量类似的题目。

现在做题遇到了 Tarjan,那么,重学,开写!

另,要想学好此算法的第一件事——膜拜 Tarjan 爷爷。

Tarjan 算法到底是什么

其实广义上有许多算法都是 Tarjan 发明的(大名鼎鼎的 Link-Cut-Tree 正是出自他手),而本文介绍的是可以解决图中强连通分量的算法。

也就是狭义的 Tarjan 算法。

什么是强连通分量

对于一个图 \(G\) 来说,一个字图中,任意两点都可以彼此到达(存在路径),这个子图就称为图 \(G\) 的强连通分量。特别地,一个点也是一个强连通分量。

算法思路

Tarjan 是基于 DFS 实现的,走过的边会形成一棵搜索树。可以看作是原图删去一些边留下来而形成的。

看个图吧:

如果我们把抛弃的边分为三个大类,可以分为:

  1. 横叉边(红)
  2. 前向边(黄)
  3. 后向边(蓝)

上图把抛弃的边画出来就是这样了:

容易发现,能够构成环的只有前向边。而我们所需要得到的连通分量,正需要环。

我们怎么知道 DFS 到什么时候是一条前向边呢?

我们可以在 DFS 过程中给每个点打一个时间戳(实际上就是 DFS 序, dfn[x]=++cnt),如此,当我们遍历某节点的儿子 \(v\) 时,\(v\) 是一个已访问过的节点,那么我们找到了后向边。

如何维护?——用两个数组

  1. dfn[i]:储存时间戳。
  2. low[i]:储存 \(i\) 点可以访问到的最高祖先的 dfn 值(因为 DFS 序由小到大,所以储存的数越小、表示 \(i\) 点访问祖先能力越强)。

特殊地,一个点访问祖先的能力再差,也可以访问到自己。

代码实现

code

int dfn[MAXN],low[MAXN],tim;
bool vis[MAXN];
int ans;
stack<int> st;
int belong[MAXN];
vector<int> G[MAXN];
void tarjan(int x)
{
    dfn[x]=low[x]=++tim;
    st.push(x);
    vis[x]=1;
    for(int i=hd[x];i;i=lt[i])
    {
        int v=en[i];
        if(!dfn[v])
        {
            tarjan(v);
            low[x]=min(low[x],low[v]);
        }
        else if(vis[v]) // 此时找到后向边,不着急操作,等待回溯以后在操作
            low[x]=min(low[x],dfn[v]);
    }
    if(dfn[x]==low[x]) // 这是根节点独有的性质
    {
        ++ans; // 看看目前已经是第几个强连通分量了
        int top;
        do
        {
            top=st.top();st.pop();
            vis[top]=0;
            belong[top]=ans; // belong[] : 某节点属于那个强连通分量
            G[ans].push_back(top); // 强连通分量有哪些成员节点。
        } while (top!=x);
    }
}

P1726 上白泽慧音

题目要求:求出最大强连通分量、并输出其成员。如数量相同,输出最小的成员集合。

此题目中,belong[] 就不需要了,存成员是必要的;按字典序输出的话,把成员丢进优先队列带走,秒了!

code

#include<bits/stdc++.h>
using namespace std;

#define int long long

const int MAXN=2e5+5;

int n,m;
int dfn[MAXN],low[MAXN],tim;
bool vis[MAXN];
int ans;
stack<int> st;
int belong[MAXN];
int su,hd[MAXN],lt[MAXN],en[MAXN];
priority_queue<int,vector<int>,greater<int>> G[MAXN];
struct node
{
    int id,sz,val;
}p[MAXN];

void add(int u,int v)
{
    en[++su]=v,lt[su]=hd[u],hd[u]=su;
}

void tarjan(int x)
{
    dfn[x]=low[x]=++tim;
    st.push(x);
    vis[x]=1;
    for(int i=hd[x];i;i=lt[i])
    {
        int v=en[i];
        if(!dfn[v])
        {
            tarjan(v);
            low[x]=min(low[x],low[v]);
        }
        else if(vis[v])
            low[x]=min(low[x],dfn[v]);
    }
    if(dfn[x]==low[x])
    {
        ++ans;
        p[ans].id=ans;
        p[ans].val=st.top();
        int top;
        do
        {
            top=st.top();st.pop();
            vis[top]=0;
            belong[top]=ans;
            p[ans].sz++;
            G[ans].push(top);
        } while (top!=x);
    }
}

signed main()
{
    scanf("%lld%lld",&n,&m);
    for(int i=1,u,v,w;i<=m;i++)
    {
        scanf("%lld%lld%lld",&u,&v,&w);
        add(u,v);
        if(w==2) add(v,u);
    }
  
    for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);

    sort(p+1,p+ans+1,[](node x,node y){
        return x.sz==y.sz?x.val<y.val:x.sz>y.sz;
    });

    printf("%lld\n",p[1].sz);
    while (!G[p[1].id].empty())
    {
        printf("%lld ",G[p[1].id].top());
        G[p[1].id].pop();
    }
  
    return 0;
}

参考文献

标签:Tarjan,int,top,算法,dfn,MAXN,low,详细,ans
From: https://www.cnblogs.com/holmes-wang/p/17985989

相关文章

  • 2024/1/24 算法笔记
    1.快速幂模板虽然前面可能写过了,但是遇到了就再贴一下。LLqmi(LLa,LLk,LLp){LLres=1%p;while(k){if(k&1)res=res*a%p;a=a*a%p;k=k>>1;}returnres;}2.最大子段和给一个数组,求其中元素总和最大......
  • PPO算法——PPOxFamily
    1.决策智能目的就是搜索最优解,方法主要有两种:从模仿中学习、从试错中学习从模仿中学习通过棋谱来学棋优势:简洁直观劣势:数据要求高,可迁移性差从试错中学习通过对弈来学习优势:可以不断提升和强化劣势:过程复杂,效率和稳定性有待提高深度强化学习——更强大、更通用、更稳定......
  • 代码随想录算法训练营第一天| 704. 二分查找、27. 移除元素。
    704.二分查找题目链接:https://leetcode.cn/problems/binary-search/文章讲解:https://programmercarl.com/0704.二分查找.html简单的二分查找法,核心是认识区间的意义,注意以下几点:middle=low+(low+high)/2;这种写法可以防止溢出。注意low和high的循环条件判断,如果是左闭右闭......
  • 重写SpringCloudGateway路由查找算法,性能提升100倍!
    如果你也在做SpringCloudGateway网关开发,希望这篇文章能给你带来一些启发背景先说背景,某油项目,通过SpringCloudGateway配置了1.6万个路由规则,实际接口调用过程中,会偶现部分接口从发起请求到业务应用处理间隔了大概5秒的时间,经排查后发现是SpringCloudGateway底层在查找对应的R......
  • day25 代码随想录算法训练营 216. 组合总和 III
    题目:216.组合总和III我的感悟:还是按照之前的套路来。多了一个参数path_sum应该是有两处剪枝,1处横线剪枝,1处纵向剪枝?或者说1处求和剪枝?1处范围剪枝?【疑问】理解难点:不剪枝的已经模的差不多了,剪枝的再看看 自己听了一遍写的:[未剪枝]classSolution:defcombina......
  • 字符串算法
    #include<bits/stdc++.h>usingnamespacestd;typedeflonglongll;constllN=1e6+10;chars1[N],s2[N];lln1,n2,nt[N],f[N];intmain(){ cin>>(s1+1)>>(s2+1); n1=strlen(s1+1),n2=strlen(s2+1); for(lli=2,j=0;i<=n2;i++){ while(j......
  • 数据挖掘||利用SQL Server 2012或者Excel 2013采用聚类和时序挖掘模型和算法,对自行车
    1.实验要求 利用SQLServer2012或者Excel2013(二者选择其一即可)进行数据挖掘实验,采用聚类和时序挖掘模型和算法,可以对附件中给定的excel数据进行聚类和时序挖掘实验,也可以采用自己采集的数据(如采用自选请说明数据来源)。 2.实验环境 操作系统:windows11;软件:Excel2019;SQLServer......
  • Unity3D Rts游戏里的群体移动算法是如何实现的详解
    前言实时战略(RTS)游戏是一种以管理和控制虚拟军队为主题的游戏类型。在这类游戏中,玩家需要控制大量的单位进行战斗、资源采集和建设等操作。其中,群体移动算法是实现这些操作的关键之一。本文将详细介绍Unity3DRTS游戏中群体移动算法的实现原理和代码实现。对惹,这里有一个游戏开......
  • 软件详细设计说明书
    ......
  • 软件详细设计说明书
    ......