Tarjan总结

在图论中,连通图基于连通的概念。在一个无向图G中,若从顶点到顶点有路径相连(当然从到也一定有路径),则称和是连通的。如果G是有向图,那么连接和的路径中所有的边都必须同向。如果图中任意两点都是连通的,那么图被称作连通图。图的连通性是图的基本性质。

有向图的所有的有向边替换为无向边,所得到的图称为原图的基图。如果一个有向图的基图是连通图,则有向图是弱连通图,弱有向图的任意两点都可以相互到达那么称这个有向图为强连通图,如果一个有向图的子图是强连通图,那么这个子图称为该有向图的强连通分量。有向图中一个单个的点也是一个强连通分量

Tarjan在无向图里可以求割点和割桥。在有向图里可以求强连通分量。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

#include<cstdio>

#include<cstring>

#include<iostream>

#include<cstring>

#include<vector>

#include<stack>

#include<algorithm>

using namespace std;

#define N 10003

int dfn[N],low[N],ins[N],Time,num;//ins是否在栈里

vector<int>gra[N];

stack<int>sta;

void Tarjan(int s)

{

    dfn[s] = low[s] = ++Time;

    sta.push(s);

    ins[s] = 1;

    for(int i=0;i<gra[s].size();i++)

    {

        int k = gra[s][i];

        if(dfn[k] == 0){

            Tarjan(k);

            low[s] = min(low[s] ,low[k]);

        }

        if(dfn[k] != 0 && ins[k] == 1){

            low[s] = min(low[s] ,dfn[k]);//low[s] = min(low[s] ,low[k]);好像也是对的

        }

    }

    if(dfn[s] == low[s])

    {

        num++;

        while(!sta.empty())//一次性弹出来的所有点属于一个强连通分量

        {

            int temp = sta.top();

            sta.pop();

            ins[temp] = 0;

            if(temp == s) break;

        }

    }

}

int main()

{

    int n,m;

    while(scanf("%d%d",&n,&m)&&(n + m))

    {

        memset(dfn,0,sizeof(dfn));

        memset(ins,0,sizeof(ins));

        memset(low,0,sizeof(low));

        while(!sta.empty()) sta.pop();

        for(int i=1;i<=n;i++) gra[i].clear();

        int a,b;

        while(m--)

        {

            scanf("%d%d",&a,&b);

            gra[a].push_back(b);

        }

        num = Time = 0;

        for(int i=1;i<=n;i++)

        {

            if(dfn[i]==0) Tarjan(i);

        }

//num就是强连通分量的个数

    }

}

1.HDU1269

裸Tarjan求强连通分量个数,跑N遍Tarjan得到的NUM就是结果

2.POJ2186

Tarjan后重建图判断出度为0的节点个数,若为1则OK。

for(int i=1;i<=n;i++)

    {

        for(int j=0;j<Gra[i].size();j++){

            int k = Gra[i][j];

            if(belong[i] != belong[k]){

                outDegree[belong[i]]++;

            }

        }

    }

其实也算不上重建图....只是求了一下各个节点的出度而已......

 

3.POJ2762 

    Tarjan + 拓扑排序

 这个题是真的烦人,都给我WA哭了有木有,从中午2点错到晚上10点,中间气的我玩了几把游戏,真的气。

 还有一个就是有人说缩晚点以后的DAG是一条链,也有好多人是这么写的,都AC了,说是dfs点的数目等于强连通块的数目就可以。

但是感觉明显不对啊,整个就是一个树,你dfs的点数肯定等于强连通块的数目啊,不懂他们怎么A的。

感觉和2186差不多,但是最多只有1000个顶点。

 我怎么这么愚蠢,居然想到要暴力!!!我太蠢啦!!!

学长一语点醒

我这都没想到-。-||


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

#include<cstdio>

#include<cstring>

#include<iostream>

#include<string>

#include<vector>

#include<stack>

#include<set>

#include<algorithm>

using namespace std;

#define N 1002

vector<int>Gra[N];

stack<int>Sta;

int map[N][N];

int dfn[N],low[N],inStack[N],belong[N],Time,cnt;

int inDegree[N];

void init()

{

    Time = cnt = 0;

    memset(dfn,0,sizeof(dfn));

    memset(low,0,sizeof(dfn));

    memset(inStack,0,sizeof(inStack));

    memset(inDegree,0,sizeof(inDegree));

    memset(belong,0,sizeof(belong));

    for(int i=0;i<N;i++) Gra[i].clear();

    memset(map,0,sizeof(map));

    while(!Sta.empty()) Sta.pop();

}

void Tarjan(int s)

{

    dfn[s] = low[s] = ++Time;

    inStack[s] = 1;

    Sta.push(s);

    for(int i=0;i<Gra[s].size();i++)

    {

        int j = Gra[s][i];

        if(dfn[j] == 0){

            Tarjan(j);

            low[s] = min(low[s], low[j]);

        }

        else if(inStack[j] == 1){

            low[s] = min(low[s], dfn[j]);

        }

    }

    if(dfn[s] == low[s])

    {

        cnt ++;

        while(!Sta.empty()){

            int temp = Sta.top(); Sta.pop();

            inStack[temp] = 0;

            belong[temp] = cnt;

            if(temp == s) break;

        }

    }

    return;

}

void tsort()

{

    for(int k=0;k<cnt;k++){

        int fuck = 0,pos;

        for(int i=1;i<=cnt;i++)

        {

            if(inDegree[i] == 0)

            {

                fuck ++;

                pos = i;

            }

        }

        if(fuck > 1){

            printf("No\n");

            return ;

        }

        inDegree[pos ] = -1;

        for(int i=1;i<=cnt;i++)

        {

            if(map[pos][i] == 1)

                inDegree[i]--;

        }

    }

    printf("Yes\n");

}

int main()

{

    int noc;

    cin>>noc;

    while(noc--)

    {

        init();

        int n,m,x,y;

        scanf("%d%d",&n,&m);

        for(int i=0;i<m;i++)

        {

            scanf("%d%d",&x,&y);

            Gra[x].push_back(y);

        }

        for(int i=1;i<=n;i++) if(dfn[i] == 0) Tarjan(i);

        if(cnt == 1) {

            printf("Yes\n");

            continue;

        }

        for(int i=1;i<=n;i++)

        {

            for(int j=0;j<Gra[i].size();j++)

            {

                int k = Gra[i][j];

                if(belong[i]!=belong[k]){

                    if(map[belong[i]][belong[k]] == 0){

                        map[belong[i]][belong[k]] = 1;

                        inDegree[belong[k]]++;

                    }

                }

            }

        }

        for(int i=1;i<=cnt;i++) printf("%d %d\n",i,inDegree[i]);

        tsort();

    }

}

来源: http://tool.oschina.net/highlight

4.HYSBZ 1179

这个就是tarjan + bfs就可以了,我感觉我的代码命名都很清晰,一看就明白了。

还有就是网上好多人都是 tarjan + spfa,但我感觉spfa没有必要,因为缩点以后的图一定是一个DAG图。他的搜索树一定是一个没有向后边的的树,所以一遍vfs     就够了。

5.最后说一下就是,这个缩点的时候,如果只用入度出度是否为0就能得到答案的话直接

for(int i=1;i<=n;i++)

    {

        for(int j=0;j<Gra[i].size();j++){

            int k = Gra[i][j];

            if(belong[i] != belong[k]){

                outDegree[belong[i]]++;//inDegree[belong[i]]++;

            }

        }

    }

就可以了。

但是假如要对入度出度进行操作就不可以了,因为强连通块1是由1,2,3节点构成,强连通块2是由4,5构成

现在缩点后块1 ----> 块2。其中节点2 ---> 4,2 ---> 5,3 ---> 5。这样下来outdegree[1] == 3;

indegree[2] == 3;这其实就错了,如果拓扑排序是不出结果的。比如上面的poj2762.

那么这个类型的就告一段落了,感觉掌握的还不错,因为本身Tarjan比较好理解(当然一开始看的时候各种网上找讲解

也是看得我一脸懵逼,一开始看还是有点不好想的)但是只要理解了以后代码,就很好打了。

时间: 2024-10-08 09:48:35

Tarjan总结的相关文章

有向图强连通分支的Tarjan算法讲解 + HDU 1269 连通图 Tarjan 结题报告

题目很简单就拿着这道题简单说说 有向图强连通分支的Tarjan算法 有向图强连通分支的Tarjan算法伪代码如下:void Tarjan(u) {dfn[u]=low[u]=++index//进行DFS,每发现一个新的点就对这个点打上时间戳,所以先找到的点时间戳越早,dfn[U]表示最早发现u的时间,low[u]表示u能到达的最早的时间戳.stack.push(u)//将U压入栈中for each (u, v) in E {if (v is not visted)//如果V点没有经历过DFS,则

【LCA/tarjan】POJ1470-Closest Common Ancestors

[题意] 给出一棵树和多组查询,求以每个节点为LCA的查询数有多少? [错误点] ①读入的时候,注意它的空格是随意的呀!一开始不知道怎么弄,后来看了DISCUSS区大神的话: 询问部分输入: scanf("%d",&m); for(int i=0;i<m;i++){ scanf(" (%d %d)",&a,&b); } 注意scanf(" 这里有一个空格 ②多组数据啊!注意这句话:The input file contents

tarjan强连通图分量

[有向图强连通分量] 有向图强连通分量的Tarjan算法 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components). 下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达.{5},{6}也分别是两个强连通分量. [Tarjan算法] Tarjan算法是基于对图深

POJ 1236 tarjan

Network of Schools Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 19613   Accepted: 7725 Description A number of schools are connected to a computer network. Agreements have been developed among those schools: each school maintains a li

【最近公共祖先Tarjan】Tarjan求LCA练习

Tarjan求LCA 这是一篇非常好的讲解,靠这个文章搞懂的~ 1 void tarjan(int u) 2 { 3 vis[u]=1; 4 for(int i=0;i<edge[u].size();i++) 5 { 6 int v=edge[u][i]; 7 if(vis[v] == 0) 8 { 9 tarjan(v); 10 p[v]=u; 11 } 12 } 13 for(int i=0;i<qy[u].size();i++) 14 { 15 int v=qy[u][i].v,id=q

UVa 12587 Reduce the Maintenance Cost(Tarjan + 二分 + DFS)

题意:n个城市(n <= 10000), 有m条边(m <= 40000),每一个城市有一个维护费用Cost(i),除此之外,每条边的维修费用为去掉该边后不能通信的城市对数与边权的积.这个费用要加到这条边的两端城市的某一个,问你全部城市的最大费用的最小值.. 思路:首先边的费用能够通过Tarjan求桥之后求得(利用桥的性质),然后就是二分答案了!对于每一个点,假设有个儿子不能维护.那么不可行,否则.试着让儿子去维护边权,假设不可行,仅仅能让父亲承担. #include <iostream

hdu 2586(Tarjan 离线算法)

How far away ?                                                                             Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)                                                                            

【转】BYV--有向图强连通分量的Tarjan算法

转自beyond the void 的博客: https://www.byvoid.com/zhs/blog/scc-tarjan 注:红色为标注部分 [有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components). 下图中,子图{1,2,3,4}为一个强连通分量,因为顶

【学习整理】Tarjan:强连通分量+割点+割边

Tarjan求强连通分量 在一个有向图中,如果某两点间都有互相到达的路径,那么称中两个点强联通,如果任意两点都强联通,那么称这个图为强联通图:一个有向图的极大强联通子图称为强联通分量.   算法可以在 的时间内求出一个图的所有强联通分量. 表示进入结点 的时间 表示从 所能追溯到的栈中点的最早时间 如果某个点 已经在栈中则更新  否则对 进行回溯,并在回溯后更新  #include<iostream> #include<cstdlib> #include<cstdio>

(转载)LCA问题的Tarjan算法

转载自:Click Here LCA问题(Lowest Common Ancestors,最近公共祖先问题),是指给定一棵有根树T,给出若干个查询LCA(u, v)(通常查询数量较大),每次求树T中两个顶点u和v的最近公共祖先,即找一个节点,同时是u和v的祖先,并且深度尽可能大(尽可能远离树根).LCA问题有很多解法:线段树.Tarjan算法.跳表.RMQ与LCA互相转化等.本文主要讲解Tarjan算法的原理及详细实现. 一 LCA问题 LCA问题的一般形式:给定一棵有根树,给出若干个查询,每个