9.求强连通分量个数(缩点)~tarjan算法

上篇讲了拓扑排序只适用于有向无环图,那么tarjan算法就是把有向有环图变成一个有向无环图的算法

上述过程也就是缩点,是将原来的一个强连通分量缩成一个点,理由很简单,我只要有了这个强连通分量内的任意一点,我就可以到达强连通分量内的任意一个点,所以把他们一个整体看成一个点,由于环或者说回路全被合并成整体了,剩下的图自然就是有向无环图了!(不明白的自己百度强连通分量定义)

先介绍两个数组,明白一下他们的意义(建议强记,然后结合下面代码来看)

dfn数组,俗称的时间戳,代表此点是第几个被遍历到的

low数组,代表能追溯到的最早的已经被遍历过的点的序号

pan数组,并查集思想,初始化为pan[i]=i

下面直接贴一下tarjan函数

stack<int> q;//建立一个栈
void tarjan(int u)
{
    low[u]=dfn[u]=++ant;//点u被遍历到了,所以把low和dfn初始化了,dfn初始化之后的值就不会在改变,但是low是可能改变的,因为此时还没有朝下遍历,不知道是否能回溯到之前被遍历过的点,所以初始化为自己的序号
    q.push(u);//u点被遍历了,扔进栈
        used[u]=1;//标记进栈
    for(int i=head[u];i;i=bian[i].qian)//以u为起点遍历相连的点
    {
        int v=bian[i].wei;
        if(!dfn[v])//dfn我初始化全为0,如果v没有被遍历过
        {
            tarjan(v);那就递归进去
            low[u]=min(low[u],low[v]);u能追溯的最早的点当然就是自己和v点能追溯到的最早的点中最早的一个了,dp的思想
        }
        else if(used[v])//如果v点已经被遍历过了,并且在栈里
        low[u]=min(low[u],low[v]);//那low[u]就和low[u]做对比
//是不是发现了盲点,除了v点被遍历过并且不在栈中,其他时候都要比较low[u]和low[v],当v点已经确定在某一强连通分量时,在比较会让low[u]变小,使结果错误
    }
    if(low[u]==dfn[u])//如果时间戳和low相等,这意味着什么,意味他能回溯到的最早的点就是他自己,u点向下搜索饶了一圈又回到了自己(或者说无法回溯到自己或者比自己更早的点),说明u就是强连通分量的一个起始搜索点,此时栈里在u点之上的点都属于这个强连通分量
    {
        while(q.top()!=u)//现在要把栈里面的点提出来
        {
                        used[q.top()]=0;//标记出栈
            pan[q.top()]=u;//此时他们的祖宗不在是自己,而是u(并查集思想)
            q.pop();//出栈
             
        }
        q.pop();//u点自己也是要出的
                used[u]=0;
    }
}

多解释一下,为什么当已经确定一个点在强连通分量里面(即used[v]=0且dfn[v]不为0的时候不能比较low[u]和low[v]),比如现在有1 2 3三个点,1指向2,2指向1,3指向1,从1开始遍历,显然1 2是一个强连通分量,1 2遍历完后得到,low[1]=dfn[1]=1,low[2]=1,dfn[2]=2,used[2]=0,used[1]=0,从3开始遍历,dfn[3]=3,3显然是一个单独的强连通分量,如果强行比较,会使得正确结果low[3]=dfn[3]=3变成dfn[3]=3,low[3]=1,显然这个答案就错误了

下面是洛谷缩点模板原题

image.png

标准的缩点题,先缩点成一个有向无环图,累加权值给强连通分量,然后用拓扑排序dp处理权值和,下面我只贴一份缩点的代码,拓扑排序有兴趣大家可以补充一下交一下这个题:https://www.luogu.com.cn/problem/P3387

#include<iostream>
#include<algorithm>
#include<string.h>
#include<stack>
using namespace std;
struct xing
{
    int qian,wei;
}bian[101000];
int n,m,cnt,head[11000],low[11000],dfn[11000],ant,ans,pan[11000],o1,o2,mem[11000];
bool used[11000];
void add(int u,int v)
{
    bian[++cnt].qian=head[u];
    bian[cnt].wei=v;
    head[u]=cnt;
}
stack<int> q;
void tarjan(int u)
{
    low[u]=dfn[u]=++ant;
    q.push(u);
    used[u]=1;
    for(int i=head[u];i;i=bian[i].qian)
    {
        int v=bian[i].wei;
        if(!dfn[v])
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(used[v])
        low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u])
    {
        ans++;
        while(q.top()!=u)
        {
            used[q.top()]=0;
            pan[q.top()]=u;
            mem[u]+=mem[q.top()];//权值累加
            q.pop();
        }
        q.pop();
        used[u]=0;
    }
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<n+1;i++)
    {
        pan[i]=i;
        cin>>mem[i];//每个点的权值
    }
    for(int i=1;i<m+1;i++)
    {
        cin>>o1>>o2;
        add(o1,o2);//连边
    }
    for(int i=1;i<n+1;i++)
    {
        if(!dfn[i])
        tarjan(i);//缩点
    }
    cout<<ans;//输出强连通分量个数
    return 0;
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,402评论 6 499
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,377评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,483评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,165评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,176评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,146评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,032评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,896评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,311评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,536评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,696评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,413评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,008评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,815评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,698评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,592评论 2 353

推荐阅读更多精彩内容

  • 前言:我们伟大的BAT承载了多少程序员的梦想,到底有多少人的向往... 然而这个“T”的面试也是经常不走寻常路,最...
    Tel_小超阅读 1,101评论 0 0
  • 数据结构与算法--图论之寻找连通分量、强连通分量 寻找无向图的连通分量 使用深度优先搜索可以很简单地找出一幅图的所...
    sunhaiyu阅读 9,546评论 2 11
  • 首先先要明确概念:强连通图意为在该图中任意两点间都能够相互到达,而强连通分量即为一个强连通图中的子图,如图中{1,...
    Ricardo_Y_Li阅读 3,052评论 0 2
  • 强连通分量 概念 1.连通:在有向图中,若存在点a到达点b的有向路径并且存在点b到达点a的有向路径,则点a和点b是...
    Bin_ZH阅读 1,031评论 0 0
  • 本文介绍图的几种基本操作:BFS,DFS,求有向图连通分量的Tarjan算法以及拓扑排序。 图的表示 一张图是由若...
    maxkibble阅读 3,448评论 0 2