图的强连通&双连通

http://www.cnblogs.com/wenruo/p/4989425.html 

强连通

强连通是指一个有向图中任意两点v1、v2间存在v1到v2的路径及v2到v1的路径。

dfs遍历一个图,会生成一颗树。每个节点按最先遍历的时间给定一个编号,用一个数组dfn表示,又叫时间戳。

然后有几个概念。

画图举例:

假设一个边是u-->v

树边:dfs遍历后生成树的边叫做树边。dfn[u] = -1 如图中<1,2> <2,3> <3,4> <2,5> <1,6> <6,7> <7,8>

前向边:dfn[u]<dfn[v] 如图中<1,4>

后向边:dfn[u]>dfn[v] 如图中<4,2>

    前向边和后向边的两点公共最先为其中一点,即u或v中一点。

横跨边:dfn[u]>dfn[v] 如图中<6,5>

定义一个数组low用来记录一个结点通过任意条树边和最多一条横向边或向后边,所能到达的最小dfn值。

当一个结点low[n] == dfn[n] n就是一个强连通的根,即n的子树是一个强连通分量。可知一个强连通分量的dfn值都是连续的。

可以用一个根唯一的表示一个强连通分量。

强连通模板:

//强连通模板(tarjan) (hdu 1269
const int N = 10005;
const int M = 100005;

struct Edge {
    int to, next;
} edge[M];
int head[N];
int cnt_edge;

void add_edge(int u, int v)
{
    edge[cnt_edge].to = v;
    edge[cnt_edge].next = head[u];
    head[u] = cnt_edge;
    cnt_edge++;
}

int dfn[N];int idx;
int low[N];
int stk[N];int top;
int kind[N];int cnt;
bool in[N];

int n, m;

void dfs(int u)
{
    dfn[u] = low[u] = ++idx;
    in[u] = true;
    stk[++top] = u;
    for (int i = head[u]; i != -1; i = edge[i].next)
    {
        int v = edge[i].to;
        if (!dfn[v])
        {
            dfs(v);
            low[u] = min(low[v], low[u]);
        }
        else if(in[v])
            low[u] = min(low[u], dfn[v]);
    }

    if (low[u] == dfn[u])
    {
        ++cnt;
        int j;
        do {
            j = stk[top--];
            in[j] = false;
            kind[j] = cnt;
        } while (j != u);
    }
}

void init()
{
    memset(dfn, 0, sizeof dfn);
    memset(head, -1, sizeof head);
    cnt_edge = 0;
    top = cnt = idx = 0;
}

int main()
{
    while (~scanf("%d%d", &n, &m))
    {
        if (n == 0 && m == 0) break;
        int a, b;
        init();
        for (int i = 0; i < m; ++i)
        {
            scanf("%d%d", &a, &b);
            add_edge(a, b);
        }

        for (int i = 1; i <= n; ++i)
        {
            if (!dfn[i]) dfs(i);
        }

        if (cnt == 1) puts("Yes");
        else puts("No");
    }
    return 0;
}

  

双连通

定义:在无向连通图中,如果删除该图的任何一个结点都不能改变该图的连通性,则该图为双连通的无向图。

个人理解就是一个双连通图没有割点,没有桥的图。

在无向图中是没有前向边和横跨边的,只有树边和后向边。

如何找到割点和桥呢?

首先对于树根,如果他有多于两个的子结点,该根结点即为割点。

对于非根节点,画图举例:

low[v]<dfn[u] low[v]==dfn[u]

虚线连接到的位置就是low[v],观察可得当low[v]<=dfn[u]时,一旦去掉u点,f和v不再连通。所以当u不是树根时,任意一个子节点v满足low[v]>=dfn[u],u就是割点。

同时,当low[v]>dfn[u],(u,v)就是桥。

边的双连通分量比较简单, poj1438 & poj3177

点的双连通分量, poj2942 & hdu3394

原文地址:https://www.cnblogs.com/wenruo/p/4989425.html