连通性问题

图的强连通

对于一个有向图顶点子集S,如果在S内任取两个顶点u和v,都能找到一条从u到v的路径,那么就称S是强连通的。

强连通分量:如果在强连通顶点集合S中加入其它任意顶点集合后,它都不再是强连通的,那么就称S是原图的一个强连通分量

1.Kosaraju

因为强连通分量内的顶点,其可达性不受变得方向的影响,因此在原图和边反向的图上分别进行一次dfs

 

#include <vector>
#include <cstdio>
#include <cstring>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
int V,E;
vector<int> G[100005];
vector<int> rG[100005];
vector<int> vs;
bool used[100005];
int in[100005],out[100005];
int x[100005],y[100005],cmp[1000005],cnt[1000005];
void addedge(int from,int to){
    G[from].push_back(to);
    rG[to].push_back(from);
}
void dfs(int v){
    int i;
    used[v]=1;
    for(i=0;i<G[v].size();i++)
    if(!used[G[v][i]])
    dfs(G[v][i]);
    vs.push_back(v);
}
void rdfs(int v,int k){
    int i;
    used[v]=1;
    cmp[v]=k;
    for(i=0;i<rG[v].size();i++)
    if(!used[rG[v][i]])
    rdfs(rG[v][i],k);
}
int scc(){
    int i,k;
    memset(used,0,sizeof(used));
    vs.clear();
    for(i=0;i<V;i++)
    if(!used[i])
    dfs(i);
    memset(used,0,sizeof(used));
    k=0;
    for(i=vs.size()-1;i>=0;i--)
    if(!used[vs[i]])
    rdfs(vs[i],k++);
    return k;
}
int main(){
    int t,i,j,ans,cas,tmp;
    scanf("%d",&t);
    for(cas=1;cas<=t;cas++){
        scanf("%d%d",&V,&E);
        for(i=0;i<V;i++){
            G[i].clear();
            rG[i].clear();
            vs.clear();
        }
        for(i=0;i<E;i++){
        scanf("%d%d",&x[i],&y[i]);
        x[i]--,y[i]--;
        addedge(x[i],y[i]);
        }
        ans=scc();
        printf("%d\n",ans);
    }
    return 0;
}

图的双联通

1.在一个无向图中,如果有一个顶点集合,删除这个顶点集合,以及这个集合中所有顶点相关联的边以后,原图变成多个连通块,就称这个点集为割点集合(割集)

点连通度:最小割点集合中的顶点数

点双联通:如果一个无向连通图的点连通度大于1,则称该图是点双联通

当这个图的点连通度为1时,则割点集合的唯一元素被称为割点,又称关节点,给出求割点的代码,以poj1144为例

 

#include <vector>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
using namespace std;
vector<int> G[105];
int dis[105],low[105],vis[105],cnt[105];
int k,kk;
void dfs(int x){
    int i,tmp;
    k++;
    dis[x]=low[x]=k,vis[x]=1;
    for(i=0;i<G[x].size();i++){
        tmp=G[x][i];
        if(!vis[tmp]){
            dfs(tmp);
            low[x]=min(low[x],low[tmp]);
            if(low[tmp]>=dis[x]&&x!=1)
            cnt[x]++;
            else if(x==1)
            kk++;
        }
        else
        low[x]=min(low[x],dis[tmp]);
    }
}
int main(){
    int n,i,a,b,ans;
    while(scanf("%d",&n)!=-1){
        if(n==0)
        break;
        for(i=0;i<=n;i++)
        G[i].clear();
        memset(vis,0,sizeof(vis));
        memset(cnt,0,sizeof(cnt));
        while(scanf("%d",&a)&&a){
            while(getchar()!='\n'){
                scanf("%d",&b);
                G[a].push_back(b);
                G[b].push_back(a);
            }
        }
        k=kk=ans=0;
        dfs(1);
        if(kk>=2)
        ans++;
        for(i=1;i<=n;i++)
        if(cnt[i]>=1)
        ans++;
        printf("%d\n",ans);
    }
    return 0;
}


2.边双联通:如果一个无向连通图的边连通度大于1,则称该图是边双联通

当这个图的边连通度为1时,则割边集合的唯一元素被称为桥,又称关节边,给出求桥的代码,以zoj2588为例

#include <vector>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
struct edge{
    int to,id,num;
    edge(int a,int b,int c){
        to=a,id=b,num=c;
    }
};
vector<edge> G[20005];
int low[20005],dis[20005],vis[20005],ans[20005];
int n,m,k,kk;
void dfs(int x,int fa){
    int i;
    vis[x]=1;low[x]=k;dis[x]=k++;
    for(i=0;i<G[x].size();i++){
        edge e=G[x][i];
        if(!vis[e.to]){
            dfs(e.to,x);
            low[x]=min(low[x],low[e.to]);
            if(low[e.to]>dis[x]&&e.num==0)
            ans[kk++]=e.id;
        }
        else if(e.to!=fa)
        low[x]=min(low[x],dis[e.to]);
    }
}
int tarjan(){
    k=kk=0;
    dfs(1,1);
    return kk;
}
int main(){
    int T,a,b,i,j,l,ans1,flag;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        for(i=0;i<=n;i++){
            vis[i]=0;
            G[i].clear();
        }
        for(i=1;i<=m;i++){
            scanf("%d%d",&a,&b);
            flag=0;
            for(j=0;j<G[a].size();j++){
                edge e=G[a][j];
                if(e.to==b){
                    flag=G[a][j].num=1;
                    for(l=0;l<G[b].size();l++){
                        edge ee=G[b][l];
                        if(ee.to==a)
                        G[b][l].num=1;
                    }
                }
            }
            if(flag==0){
                G[a].push_back(edge(b,i,0));
                G[b].push_back(edge(a,i,0));
            }
        }
        ans1=tarjan();
        printf("%d\n",ans1);
        sort(ans,ans+kk);
        for(i=0;i<kk;i++){
        if(i!=kk-1)
        printf("%d ",ans[i]);
        else
        printf("%d\n",ans[i]);
        }
        if(T)
        printf("\n");
    }
    return 0;
}


 

 

 

你可能感兴趣的:(源码,模板,ACM,图论)