Tarjan算法及其应用

Tarjan算法及其应用

引入

tarjan算法可以在图上求解LCA,强连通分量,双联通分量(点双,边双),割点,割边,等各种问题。

这里简单整理一下tarjan算法的几个应用。

LCA

http://www.cnblogs.com/mjtcn/p/6852646.html

强联通分量

有向图的

强联通:在一个有向图G里,设两个点 a b 发现,由a有一条路可以走到b,由b又有一条路可以走到a,我们就叫这两个顶点(a,b)强连通。

强连通图: 如果 在一个有向图G中,每两个点都强连通,我们就叫这个图,强连通图。

强连通分量:在一个有向图G中,有一个子图,这个子图每2个点都满足强连通,我们就叫这个子图叫做 强连通分量 [分量:把一个向量分解成几个方向的向量的和,那些方向上的向量就叫做该向量(未分解前的向量)的分量]

http://www.cnblogs.com/mjtcn/p/7599217.html

 

边双联通分量

无向图的

边双联通图:如果在一个无向图中,任意两点至少存在两条不重复路径,则称该图为边双连通的。

边双联通分量:边双连通的极大子图称为边双连通分量

原理和强联通分量的求法差不多。

 1 void tarjan(int u,int fa) {
 2     dfn[u] = low[u] = ++tn;
 3     st[++top] = u;
 4     vis[u] = true;
 5     for (int i=head[u]; i; i=e[i].nxt) {
 6         int v = e[i].to;
 7         if (!dfn[v]) {
 8             tarjan(v);
 9             low[u] = min(low[u],low[v]);
10         }
11         else if (vis[v] && v!=fa) 
12             low[u] = min(low[u],dfn[v]);
13     }
14     if (dfn[u] == low[u]) {
15         ++cnt;
16         do {
17             vis[st[top]] = false;
18             bel[st[top]] = cnt;
19             top--;
20         } while (st[top+1] != u);
21     }
22 }
View Code

简单点可以这样写,low数组可以有bel数组的作用

 1 void tarjan(int u,int fa) {
 2     dfn[u] = low[u] = ++tn;
 3     for (int i=head[u]; i; i=e[i].nxt) {
 4         int v = e[i].to;
 5         if (!dfn[v]) {
 6             tarjan(v,u);
 7             low[u] = min(low[u],low[v]);
 8         }
 9         else if (dfn[v] < dfn[u] && v != fa) 
10             low[u] = min(low[u],dfn[v]);
11     }
12 }
View Code

点双联通分量

无向图的

 留坑

 

割点

图的割点

在一个无向连通图中,如果删除某个顶点后,连通分量数目增加,称这样的点为割点(或者称割顶)。

朴素的求法O(n(n+m)),尝试删除每个点,dfs判断是否联通。

tarjan算法复杂度O(n+m),线性!!!

割点的条件:

  • 根节点:它的子节点中有多个联通块。
  • 非根节点:点u及其后代中没有点连向u的祖先(可以连回u)

所以只要让low[v] >= dfn[u]即可。

 1 void tarjan(int u,int fa) {
 2     low[u] = dfn[u] = ++tn;
 3     int cnt_son = 0;
 4     for (int i=head[u]; i; i=e[i].nxt) {
 5         int v = e[i].to;
 6         if (!dfn[v]) {
 7             cnt_son++;
 8             tarjan(v,u);
 9             low[u] = min(low[u],low[v]);
10             if (low[v] >= dfn[u]) // 第二种情况 
11                 iscut[u] = true;
12         }
13         else if (dfn[v] < dfn[u] && v != fa) 
14             low[u] = min(low[u],dfn[v]);
15     }
16     if (fa<0 && cnt_son==1) iscut[u] = false; // 第一种情况 
17 }
View Code

 

 

割边

无向图

在一个无向连通图中,如果删除某条边后,图不再连通,这条边就是割边(桥)。

如果u的一个子节点v,它的所有子节点及其自己都不能连回u的祖先,这里包括也不能连向u,那么u-v,就是一个割边

 代码只要改一个地方,low[v] > dfn[u]

 1 void tarjan(int u,int fa) {
 2     dfn[u] = low[u] = ++tn;
 3     for (int i=head[u]; i; i=e[i].nxt) {
 4         int v = e[i].to;
 5         if (!dfn[v]) {
 6             tarjan(v,u);
 7             low[u] = min(low[u],low[v]);
 8             if (low[v] > dfn[u]) {
 9                 printf("%d %d\n",u,v);
10             }
11         }
12         else if (dfn[v] < dfn[u] && v != fa) 
13             low[u] = min(low[u],dfn[v]);
14     }
15 }
View Code

 

 

例题

poj 3352 Road Construction

求最少添加几条边才能使所给无向图变成边双连通图。

求出边双,缩点成一个树,之后统计树上的点度数为1的点的个数cnt,(cnt+1)/2就是答案。

定理:任意一颗无向图的树,成为双连通图,则需要增加的边数为(这棵树上所有度数为1的结点的个数+1)/2。

 

luogu P2746 [USACO5.3]校园网Network of Schools 

1.求最少让几个人知道就可以做到让所有的人都知道信息,最少知道的人的数目即为缩完点后入度为零的点的个数

2.最少加入几条边就可以使一个树变成一个强连通图,加的边的条数即为缩完点后 Max(入度为零的点的个数,出度为零的点的个数)

定理:任意一棵有向图的树,成为强联通分量,则需要增加的边数为max(入度为0的点的个数,出度为0的点的个数)

 

P3119 [USACO15JAN]草鉴定Grass Cownoisseur

求改变一条边的方向,有向图中包含1的强联通分量最大。

缩点所有的强联通分量,然后拓扑求最长链到1的距离,枚举改那条边

 

P2515 [HAOI2010]软件安装

tarjan缩点+树形dp

 

 

 

 

posted @ 2017-11-25 13:29  MJT12044  阅读(707)  评论(0编辑  收藏  举报