【专题】图的连通性问题无向图的点连通性的求解及应用

1.求割点:

(1).朴素的方法:n^3

(2).Tarjan求割点:n^2

 顶点u是割点的充要条件:

1.如果顶点u是深度优先搜索生成树的根,则u至少有两个子女.

2.如果u不是生成树的根,则它至少有一个子女w,从w出发,不可能通过w、w的子孙,以及一条回边组成的路径到达u的祖先.

(low[w]>=dfn[u]).

去掉割点,将原来的连通图分成了几个连通分量?

1.如果割点u是根结点,则有几个子女,就分成了几个连通分量.

2.如果割点u不是根结点,则有d个子女w,使得low[w]>=dfn[u],则去掉该结点,分成了d+1个连通分量。

下面是一段求割点的代码:

View Code
/*Source Code
Problem: 1523  User: HEU_daoguang
Memory: 4628K  Time: 16MS
Language: G++  Result: Accepted
Source Code*/
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
#define min(a,b) ((a)>(b)?(b):(a));
int Edge[1001][1001];
int visited[1001];
int nodes;
int tmpdfn;
int dfn[1001];
int low[1001];
int son;
int subnets[1001];
void init()
{
    low[1]=dfn[1]=1;
    tmpdfn=1;
    son=0;
    memset(visited,0,sizeof(visited));
    visited[1]=1;
    memset(subnets,0,sizeof(subnets));
}
void dfs(int u)
{
    for(int v=1;v<=nodes;v++)
    {
        if(Edge[u][v])
        {
            if(!visited[v])
            {
                visited[v]=1;
                tmpdfn++;
                dfn[v]=low[v]=tmpdfn;
                dfs(v);
                low[u]=min(low[u],low[v]);
                if(low[v]>=dfn[u])
                {
                    if(u!=1)
                        subnets[u]++;
                    if(u==1) son++;
                }
            }
            else
            {
                low[u]=min(low[u],dfn[v]);
            }
        }
    }
}

int main()
{
    int i;
    int u,v;
    int find;
    int number=1;
    while(1)
    {
        scanf("%d",&u);
        if(u==0)
        {
            break;
        }
        memset(Edge,0,sizeof(Edge));
        nodes=0;
        scanf("%d",&v);
        if(u>nodes) nodes=v;
        if(v>nodes) nodes=u;
        Edge[u][v]=Edge[u][v]=1;
        while(1)
        {
            scanf("%d",&u);
            if(u==0) break;
            scanf("%d",&v);
            if(u>nodes) nodes=u;
            if(v>nodes) nodes=v;
            Edge[u][v]=Edge[v][u]=1;
        }

        if(number>1) printf("\n");
        printf("Network #%d\n",number);
        number++;
        init();
        dfs(1);
        if(son>1) subnets[1]=son-1;
        find=0;
        for(i=1;i<=nodes;i++)
        {
            if(subnets[i])
            {
                find=1;
                printf("  SPF node %d leaves %d subnets\n",i,subnets[i]+1);
            }
        }
        if(!find) printf("  No SPF nodes\n");
    }
    return 0;
}

 2.求双连通分量:

双连通分量特性:无割点,删除任何一点都不会影响其他点之间的通信。

双连通图:无割点的图.

下面是一段求双连通分量的代码:

View Code
#include <string.h>
#include <iostream>
#include <stdio.h>
#define min(a,b) ((a)>(b)?(b):(a))
#define MAXN 20
#define MAXM 40
struct edge{
    int u,v;
}edges[MAXM];
int top;
int Edge[MAXN][MAXN];
int vis[MAXN];
int n,m;
int tmpdfn;
int dfn[MAXN];
int low[MAXN];
void dfs(int u){
    for(int v=1;v<=n;v++){
        if(Edge[u][v]==1){
            edge t;
            t.u=u;
            t.v=v;
            edges[++top]=t;
            Edge[u][v]=Edge[v][u]=2;
            if(!vis[v]){
                vis[v]=1;
                tmpdfn++;
                dfn[v]=low[v]=tmpdfn;
                dfs(v);
                low[u]=min(low[u],low[v]);
                if(low[v]>=dfn[u]){
                    bool firstedge=true;
                    while(1){
                        int flag=0;
                        if(top<0) break;
                        if(firstedge) firstedge=false;
                        else printf(" ");
                        printf("%d-%d",edges[top].u,edges[top].v);
                        if((edges[top].u==u&&edges[top].v==v) || (edges[top].u==v&&edges[top].v==u)) flag=1;
                        edges[top].u=0;edges[top].v=0;
                        top--;
                        if(flag) break;
                    }
                    printf("\n");
                }
            }
            else low[u]=min(low[u],dfn[v]);
        }
    }
}
int main()
{
    int u,v;
    int number=1;
    while(scanf("%d%d",&n,&m)!=EOF){
        if(n==0 && m==0) break;
        memset(Edge,0,sizeof(Edge));
        for(int i=1;i<=m;i++){
            scanf("%d%d",&u,&v);
            Edge[u][v]=Edge[v][u]=1;
        }
        if(number>1) printf("\n");
        number++;
        low[1]=dfn[1]=1;
        tmpdfn=1;
        memset(vis,0,sizeof(vis));
        vis[1]=1;
        memset(edges,0,sizeof(edges));
        top=-1;
        dfs(1);
    }
    return 0;
}
/*
input
7 9
1 2
1 3
1 6
1 7
2 3
2 4
2 5
4 5
6 7
output
5-2 4-5 2-4
3-1 2-3 1-2
7-1 6-7 1-6
*/

 3.求顶点连通度k(G),求最小割顶集:

A,B为任意不相邻的两点.

P(A,B)为A-B之间的独立轨最大条数,最少要删除P(A,B)个点才能使A,B不连通.

求P(A,B)->构图+最大流

结论:一个图的任意A,B两点间的P(A,B)的最小值即为该图的顶点连通度k(G),即最小割顶集.

网络流求解:O(n^4)见代码:

View Code
/*Source Code
Problem: 1966  User: HEU_daoguang
Memory: 1056K  Time: 16MS
Language: G++  Result: Accepted
Source Code*/
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
#define V 300
#define E 30000
#define inf 0xffff
int map[V][2];
int flag[V][V];
struct Edge
{
    int u,v,c,next;
}edge[E];
int n,m,cnt;
int dist[V];
int head[V];
int que[V];
int sta[V];
int s,t;
void init(){
    cnt=0;
    memset(head,-1,sizeof(head));
}
void addedge(int u,int v,int c){
    edge[cnt].u=u;edge[cnt].v=v;edge[cnt].c=c;
    edge[cnt].next=head[u];head[u]=cnt++;
    edge[cnt].u=v;edge[cnt].v=u;edge[cnt].c=0;
    edge[cnt].next=head[v];head[v]=cnt++;
}

int dinic(int s,int t){
    int ans=0;
    while(true){
        int left,right,u,v;
        memset(dist,-1,sizeof(dist));
        left=right=0;
        que[right++]=s;
        dist[s]=0;

        while(left<right){
            u=que[left++];
            for(int k=head[u];k!=-1;k=edge[k].next){
                u=edge[k].u;
                v=edge[k].v;
                if(edge[k].c > 0 && dist[v]==-1){
                    dist[v]=dist[u]+1;
                    que[right++]=v;
                    if(v==t){
                        left=right;
                        break;
                    }
                }
            }
        }

        if(dist[t]==-1) break;

        int top=0;
        int now=s;

        while(true){
            if(now!=t){
                int k;
                for(k=head[now];k!=-1;k=edge[k].next){
                    if(edge[k].c>0 && dist[edge[k].v]==dist[edge[k].u]+1) break;
                }
                if(k!=-1){
                    sta[top++]=k;
                    now=edge[k].v;
                }
                else{
                    if(top==0) break;
                    dist[edge[sta[--top]].v]=-1;
                    now=edge[sta[top]].u;
                }
            }
            else{
                int flow=inf,ebreak;
                for(int i=0;i<top;i++){
                    if(flow>edge[sta[i]].c){
                        flow=edge[sta[i]].c;
                        ebreak=i;
                    }
                }
                ans+=flow;
                for(int i=0;i<top;i++){
                    edge[sta[i]].c-=flow;
                    edge[sta[i]^1].c+=flow;
                }
                now=edge[sta[ebreak]].u;
                top=ebreak;
            }
        }
    }
    return ans;
}

void build(int x,int y,int ver,int n,int m){
    init();
    for(int i=1;i<=n;i++){
        addedge(i,i+n,1);
    }
    for(int i=0;i<m;i++){
        addedge(map[i][0]+n,map[i][1],inf);
        addedge(map[i][1]+n,map[i][0],inf);
    }
    addedge(x,x+n,inf);
    addedge(y,y+n,inf);
}

int main()
{
    //freopen("in.txt","r",stdin);
    while(scanf("%d%d",&n,&m)!=EOF){
        int a,b;
        int ver=n*2+1;
        memset(map,0,sizeof(map));
        memset(flag,0,sizeof(flag));
        for(int i=0;i<m;i++){
            scanf(" (%d,%d)",&a,&b);
            a++,b++;
            map[i][0]=a,map[i][1]=b;
            flag[a][b]=1;
        }
        if(m==0){
            if(n==1) printf("1\n");
            else printf("0\n");
            continue;
        }
        int pre,ans=inf,sign=0;
        for(int i=1;i<=n;i++){
            for(int j=i+1;j<=n;j++){
                if(!flag[i][j]){
                    sign=1;
                    build(i,j,ver,n,m);
                    ans=min(ans,dinic(i,j+n));
                }
            }
        }
        if(sign){
            printf("%d\n",ans);
        }
        else{
            printf("%d\n",n);
        }
    }
    return 0;
}

Stoer-Wagner算法:O(n^3),见博客L:

http://www.cnblogs.com/ylfdrib/archive/2010/08/17/1801784.html

原文地址:https://www.cnblogs.com/markliu/p/2515422.html