图的联通-割点和桥

求割点

DFS搜索树:用DFS对图进行遍历时,按照遍历次序的不同,我们可以得到一棵DFS搜索树。

树边:(称为父子边),可理解为在DFS过程中访问未访问节点时所经过的边。

回边:(返祖边后向边),可理解为在DFS过程中遇到已访问节点时所经过的边。

该算法是R.Tarjan发明的。观察DFS搜索树,我们可以发现有两类节点可以成为割点:

  1. 对根节点u,若其有两棵或两棵以上的子树,则该根结点u为割点;
  2. 对非叶子节点u(非根节点),若其有一子树的节点均没有指向u的祖先节点的回边,说明删除u之后,根结点与u的子树的节点不再连通;则节点u为割点。

http://cogs.pro/cogs/problem/problem.php?pid=8

#include<cstdio>
#include<algorithm>
using namespace std;
struct Edge{
    int to,next;
}edge[10005];
int fi[105],top,low[105],fa[105],dfn[105],kid[105];
bool vis[105],cut[105];
void add_edge(int from,int to){
    edge[++top].next=fi[from],fi[from]=top,edge[top].to=to;
    edge[++top].next=fi[to],fi[to]=top,edge[top].to=from;
}
void dfs(int x){
    //记录dfs遍历次序
    static int counter = 0;
    dfn[x]=low[x]=++counter;
    vis[x]=1;
    if(x==4)
    x=4;
    for(int to = fi[x];to;to=edge[to].next){
        int v=edge[to].to;
        if(!vis[v]){//节点v未被访问,则(u,v)为树边
            fa[v]=x;kid[x]++;
            dfs(v);
            low[x]=min(low[x],low[v]);
            if(low[v]>=dfn[x])cut[x]=1;
        }
        else if(v!=fa[x]){//节点v已访问,则(u,v)为回边
            low[x]=min(low[x],dfn[v]);
        }
    }
}
int main(){
    freopen("gd.in","r",stdin);
    freopen("gd.out","w",stdout);
    int n,a,b,ans=0;scanf("%d",&n);
    while(~scanf("%d%d",&a,&b))add_edge(a,b);
    dfs(1);
    if(kid[1]<2)cut[1]=0;
    for(int i=1;i<=n;i++){
        if(cut[i])ans++;
    }
    printf("%d\n",ans);
    for(int i=1;i<=n;i++){
        if(cut[i])printf("%d\n",i);
    }
    return 0;
}

求桥

对于一条边(u,v),u为祖先,如果v的所有子树都没有指向比v先遍历到的点则(u,v)为桥

posted @ 2017-05-19 21:03  Bennettz  阅读(215)  评论(0编辑  收藏  举报