【模板】求无向图中所有的割点&点双连通分量&缩点

#include
#include
using namespace std;
#define _rep(i,a,b) for(int i=(a);i<=(b);i++)
const int N=1e5+10;
struct Edge{
    int v,nx;
}edge[N<<1];
int dfn[N],low[N],n,m,head[N],tot,num,root;
void addedge(int u,int v)
{
    edge[++tot].v=v;
    edge[tot].nx=head[u];
    head[u]=tot;
}
bool cut[N];
void tarjan(int u)
{
    dfn[u]=low[u]=++num;
    int flag=0;
    for(int i=head[u];i;i=edge[i].nx)
    {
        int v=edge[i].v;
        if(!dfn[v])
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
            if(low[v]>=dfn[u])
            {
                flag++;
                if(u!=root||flag>1)cut[u]=true;
            }
        }
        else low[u]=min(low[u],dfn[v]);
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    tot=1;
    int u,v;
    _rep(i,1,m){scanf("%d%d",&u,&v);if(u!=v)addedge(u,v),addedge(v,u);}
    _rep(i,1,n)if(!dfn[i])root=i,tarjan(i);
    _rep(i,1,n)if(cut[i])printf("%d ",i);
    puts("are cut-vertexes");
    return 0;
}

为了求出“点双连通分量”,需要在Tarjan算法的过程中维护一个栈,并按照如下方法维护栈中的元素:
1.当一个节点第一次被访问时,把该节点入栈。
2.当判定是割点时,无论x是否为根都要
(1)从栈顶不断弹出节点,直至节点y被弹出。
(2)刚才弹出的所有节点与节点x一起构成一个v-DCC。

void tarjan(int x)
{
    dfn[x]=low[x]=++num;
    stack[++top]=x;
    if(x==root&&head[x]==0)//孤立点 
    {
        dcc[++cnt].push_back(x);
        return;
    }
    int flag=0;
    for(int i=head[x];~i;i=edge[i].nx)
    {
        int v=edge[i].v;
        if(!dfn[v])
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
            if(low[v]>=dfn[u])
            {
                flag++;
                if(u!=root||flag>1)cut[u]=true;
                cnt++;
                int z;
                do{
                    z=stack[top--];
                    dcc[cnt].push_back(z);
                }while(z!=v);
                dcc[cnt].push_back(u);
            }
        }
        else low[u]=min(low[u],dfn[v]);
    }
}
//以下代码片段加在求割点程序的main函数中
_rep(i,1,cnt)
{
    printf("e-DCC #%d",i);
    for(int j=0;jprintf(" %d",dcc[i][j]);
    puts("");   
} 

v-DCC的缩点

//给每个割点一个新的编号 
num=cnt;
_rep(i,1,n)if(cut[i])new_id[i]=++num;
//建新图,从每个v-DCC到它包含的所有割点连边
tc=1;
_rep(i,1,cnt)_rep(j,0,dcc[i].size())
{
    int u=dcc[i][j];
    if(cut[u])add_c(i,new_id[u]),add_c(new_id[u],i);
    else c[x]=i;//除割点外,其他点仅属于1个v-DCC 
 } 
printf("缩点之后的森林,点数%d,边数%d\n",num,tc/2);
printf("编号1~%d的为原图的v-DCC,编号>%d的为原图割点\n",cnt,cnt);
for(int i=2;i2)printf("%d %d\n",vc[i^1],vc[i]);

你可能感兴趣的:(模板)