POJ 3352 边双联通分量 tarjan 完整模板

版权声明:布呗之路的守望者 https://blog.csdn.net/hypHuangYanPing/article/details/81986145
/**
链接:http://poj.org/problem?id=3352
tarjan 边双联通分量求解;
题意:至少加几条边使原无向图边双联通。
双连通分量的求解:为缩点后图叶子节点个数(leaf)  (leaf+1)/2;
*/

#include<iostream>
#include<algorithm>
#include<vector>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;

const int maxn=1e5+7;

int top,bcnt,dcnt;
int sta[maxn],dfn[maxn],low[maxn],belong[maxn];
bool ins[maxn];
int out[maxn];
int u,v,n,m;

std::vector<int> G[maxn];
std::vector<int> GG[maxn];

int parent[maxn];//节点关系;
int iscut[maxn];//当前节点是否为割点;
int cut_num;//割点数量;

void dfs(int u){
    dfn[u]=low[u]=++dcnt;
    ins[u]=true;
    sta[top++]=u;
    int child=0;
    int SZ=G[u].size();
    //for(int v:G[u]){
    for(int i=0;i<SZ;i++){
        int v=G[u][i];

        if(v==parent[u]) continue;//对于无向图而言的自环;

        if(dfn[v]==0) {
            child++;
            parent[v]=u;
            dfs(v); 
            low[u]=min(low[u],low[v]);
            if((!iscut[u]&&parent[u]!=-1&&low[v]>=dfn[u])||(parent[u]==-1&&child>=2&&!iscut[u])) iscut[u]=1,cut_num++;//非根节点low[v]>=dfn[u],当前节点为根节点 且出度为2
        }
        else if(ins[v]) low[u]=min(low[u],dfn[v]);
    }
    /*******************************缩点操作部分 栈记录可能构成强联通分量的点**************/
    if(dfn[u]==low[u]) {
        ++bcnt;
        int pos;
        do{
            pos=sta[--top];
            ins[pos]=false;
            belong[pos]=bcnt;
        }while(pos!=u);
    }
    return ;
}

void tarjan(){
    bcnt=top=dcnt=cut_num=0;
    memset(dfn,0,sizeof(dfn));
    memset(ins,0,sizeof(ins));
    memset(out,0,sizeof(out));
    memset(iscut,0,sizeof(iscut));
    memset(parent,-1,sizeof(parent));
    for(int i=1;i<=n;i++) if(dfn[i]==0) dfs(i);

    /***缩点后图变为GG操作*********************************/
    for(int i=1;i<=n;i++) GG[i].clear();
    for(int i=1;i<=n;i++){
        int SZ=G[i].size();
        //for(int pos:G[i]){
        for(int j=0;j<SZ;j++){
            int pos=G[i][j];
            int u=belong[i];
            int v=belong[pos];
            if(u!=v) GG[u].push_back(v),++out[u];
        }
    }
}

int vis[1010][1010];

int main () {
    scanf("%d %d",&n,&m);
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=m;i++){
        scanf("%d %d",&u,&v);
        if(vis[u][v]) continue;vis[u][v]=vis[v][u]=1; //对于存在重复的边而言;
        G[u].push_back(v),G[v].push_back(u);
        
    }
    tarjan();
    int ret=0;
    for(int i=1;i<=bcnt;i++) if(out[i]==1) ret++;
    printf("%d\n",(ret+1)/2);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/hypHuangYanPing/article/details/81986145