Tarjan 算法(超详细!!)

推荐在 cnblogs 上阅读

Tarjan 算法

前言

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

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

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

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

Tarjan 算法到底是什么

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

也就是狭义的 Tarjan 算法。

什么是强连通分量

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

算法思路

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

看个图吧:

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

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

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

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

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

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

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

  1. dfn[i]:储存时间戳。
  2. low[i]:储存 i i i 点可以访问到的最高祖先的 dfn 值(因为 DFS 序由小到大,所以储存的数越小、表示 i i 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;
}

参考文献

  • [1] _H1kar1,题解 P1726 【上白泽慧音】

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/345636.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

防御课程—华为USG6000V1的配置实验(一)

实验拓扑&#xff1a; 实验分析 由实验拓扑图需求分析可知我们在生产区和办公区需要用到子接口技术 实验配置 在Cloud1上配置 在DMZ区域配置 在server1上配置在server2上配置在防火墙上进行的配置 由实验拓扑图可知防火墙与DMZ区域相连的接口为GigabitEthernet1/0/0接口 …

初识SpringBoot

SpringBoot以约定大于配置的核心思想,默认帮我们进行了很多设置,简单来说就是SpringBoot其实不是什么新的框架&#xff0c;它默认配置了很多框架的使用方式&#xff0c;就像maven整合了所有的jar包&#xff0c;spring boot整合了所有的框架 。 创建的包一定要在项目主程序入口…

【排序算法】C语言实现归并排序,包括递归和迭代两个版本

文章目录 &#x1f680;前言&#x1f680;归并排序介绍及其思想&#x1f680;递归实现&#x1f680;迭代实现 &#x1f680;前言 大家好啊&#xff01;阿辉接着更新排序算法&#xff0c;今天要讲的是归并排序&#xff0c;这里阿辉将讲到归并排序的递归实现和迭代实现&#xff…

CPU中的算术逻辑单元(ALU)

ALU有2个单元&#xff0c;1个算术单元和1个逻辑单元 算数单元 1 bit加法 半加器 由一个异或门&#xff08;XOR&#xff09;和与门&#xff08;AND&#xff09;两个逻辑门构成&#xff0c;异或门表示无进位加法&#xff08;sum&#xff09;&#xff0c;而与门表示进位&…

k-Wave仿真例程:创建超声换能器并绘制声场分布

k-Wave介绍 k-Wave软件是为了模拟超声波在1D、2D或3D中的传播。 应用示例包括&#xff1a; - 均匀和非均匀介质中的传播 - 模拟各种类型的传感器 - 模拟多普勒效应 - 衍射、折射和反射 - 光声、超声成像 - 波束合成、成像重建 - 模拟弹性波 安装k-Wave 安装k-Wave需要几个步…

基于springboot+vue的小徐影城管理系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…

移动端测试如何学,超详细的APP测试攻略送上

前言 随着手机应用市场发展的逐渐成熟&#xff0c;手机APP已经渗透到人们的吃穿住行生活&#xff0c;比如手机支付APP、通讯APP、各大应用软件等&#xff0c;关于手机APP安全性能的重要性不言而喻。 鉴于此&#xff0c;做好手机APP测试对于软件开发方把控产品质量有着重要意义…

计算机408真的很难吗❓|深度分析+实操上岸规划

在下面这篇文章中&#xff0c;LUCEN详细分析了24考研的难度以及25考研人该怎么办 24考研计算机很难&#xff01;25考研你就这么干 如果你对于计算机考研择校有任何疑问&#xff0c;那么下面这篇文章一定能够帮助你&#xff1a; 计算机择校指南&#xff0c;内含300所院校 如…

Linux命令-top

1、top命令简介 top命令是linux系统常用命令之一&#xff0c;能够实时显示系统各个进程的资源占用情况&#xff0c;类似于windows系统的任务管理器。 需要注意的是&#xff1a;top命令监控的最小单位是进程&#xff0c;如果想监控更小单位时&#xff0c;就需要用到ps或者nets…

代码评审——随机数Random问题

问题描述&#xff1a; 为了获取唯一值&#xff0c;经常会依赖产生随机数来保证唯一性。在获取随机数时&#xff0c;如果使用错误的方法&#xff0c;会比较低效。 可以参考以下代码&#xff1a; public static String geneRundomNo(){Random rnew Random();int numr.nextInt(…

springboot114基于多维分类的知识管理系统

简介 【毕设源码推荐 javaweb 项目】基于springbootvue 的基于多维分类的知识管理系统 适用于计算机类毕业设计&#xff0c;课程设计参考与学习用途。仅供学习参考&#xff0c; 不得用于商业或者非法用途&#xff0c;否则&#xff0c;一切后果请用户自负。 看运行截图看 第五章…

Server-Sent Events(SSE)简单实现实时通信

Server-Sent Events&#xff08;SSE&#xff09;是一种基于HTTP的实时通信协议&#xff0c;它允许服务器向客户端推送信息。相比于传统的轮询方式&#xff0c;SSE 提供了更加轻量级和实时的通信机制。在本文中&#xff0c;我们将深入浅出地介绍如何简单实现 Server-Sent Events…

在上海做程序员这么多年,退休后我的工资是多少?

大家好&#xff0c;我是拭心。 最近看到一个很可惜的事&#xff1a;有个阿姨在深圳缴纳了 12 年社保&#xff0c;第 13 年家里突然有事不得不回老家&#xff0c;回去后没再缴纳社保&#xff0c;结果退休后无法领退休工资&#xff0c;还得出来打工赚钱。 之所以这样&#xff0…

STL常用容器—stack与queue容器(栈与队列)

STL常用容器—stack与queue容器&#xff08;栈与队列&#xff09; stack容器1. stack容器模型图2. stack 基本概念3. stack 常用接口 queue 容器1. queue 容器模型图2. queue 基本概念3. queue 常用接口 参考博文1&#xff1a;&#xff1c;C&#xff1e; stack与queue容器概念模…

这种环境下腾讯64亿在北京拿地?

近期&#xff0c;金融市场出现较大波动&#xff0c;A股指数跌至2700点&#xff0c;同时恒生指数也下滑至15000点&#xff0c;引发了社会各界的关注和思考。与此同时&#xff0c;腾讯以64.2亿元拿下北京海淀区地块&#xff0c;马云和蔡崇信又增持阿里股票&#xff0c;这一系列的…

【Java网络编程01】网络原理初识

【Java网络编程01】网络原理初识 1. 网络通信基础概念 网络通信&#xff1a;网络互连的目的就是网络通信&#xff0c;即网络数据传输&#xff0c;再直白点而言就是不同主机的不同进程之间基于网络进行数据的传输交互。 那么&#xff0c;在组建的网络上有各种各样的主机&#…

【Conda】超详细的linux-conda环境安装教程

背景 最近被python各个版本环境整的头晕目眩&#xff0c;本来就不是专长做python的&#xff0c;切换各种版本着实不好操作&#xff0c;因此想到了conda这个好工具&#xff0c;以下是对conda的相关理解和搭建的详细过程&#xff0c;做个记录。 Conda简介 Conda是在Windows、m…

3.Eureka注册中心

3.Eureka注册中心 假如我们的服务提供者user-service部署了多个实例&#xff0c;如图&#xff1a; 大家思考几个问题&#xff1a; order-service在发起远程调用的时候&#xff0c;该如何得知user-service实例的ip地址和端口&#xff1f;有多个user-service实例地址&#xff0…

Redis - redis.windows.conf配置文件及RDB和AOF数据持久化方案

Redis - redis.windows.conf配置文件及RDB和AOF数据持久化方案 Redis的高性能是由于其将所有数据都存储在了内存中&#xff0c;为了使Redis在重启之后仍能保证数据不丢失&#xff0c;需要将数据从内存中同步到硬盘中&#xff0c;这一过程就是持久化。 Redis支持两种方式的持久化…