POJ3177:利用Tarjan求无向图的边双连通分支

连通图去掉所有的桥(割边)之后,剩下的就是一块儿一块儿的边双连通分支了

那么这道题的描述是给定无向图G,问至少加入多少条边才能让原图成为一个双连通图

这个题的做法是利用Tarjan求出图中的所有桥,以桥为界限分出来的就是一个一个边的双连通分量

然后要做的是缩点,把所有的双连通分量缩成点,这样做了之后,会形成一棵树

那么答案要求至少要添加多少条边才能让原图成为双连通图,只要把树的叶子节点都连接起来就好了

我们最少要连的边=(叶子节点数+1)/2

这个题在初识的时候可以看成Tarjon强连通缩点和Tarjon求桥的一个结合

更深入的认识等以后总结的时候再说

int n,m,cnt,deep,bridge,sum,top,ans;
int g[maxn],dfn[maxn],low[maxn],col[maxn],vis[maxn],st[maxn],deg[maxn];
struct Edge{int t,next;bool cut;}e[maxm];

col在这里面是双连通块儿,deg是点的度数,用来统计缩点图(这里是树)的叶子结点

inline int change(int x)
{
    if(x%2==0)
        x--;
    else
        x++;
    return x;
}

然后双向边加入的时候是正好一奇一偶,如果初始边的编号为0,的话,亦或1就好了,如果初始为1 就要这么处理一下子

然后是Tarjan,无向图版本的

void tarjan(int u,int fa)
{
    dfn[u]=++deep;low[u]=deep;
    vis[u]=1;
    st[++top]=u;
    for(int tmp=g[u];tmp;tmp=e[tmp].next)
    {
        int v=e[tmp].t;if(v==fa) continue;
        if(!dfn[v])
        {
            tarjan(v,u);
            low[u]=min(low[u],low[v]);
            if(low[v]>dfn[u])
            {
                bridge++;
                e[tmp].cut=true;
                int tmp1=change(tmp);
                e[tmp1].cut=true;
            }
        }
        else if(vis[v]) low[u]=min(low[u],dfn[v]);
    }
    if(dfn[u]==low[u])
    {
        col[u]=++sum;vis[u]=0;
        while(st[top]!=u)
        {
            col[st[top]]=sum;
            vis[st[top--]]=0;
        }
        top--;
    }
}

然后是缩点和统计结果的过程

for(u=1;u<=n;u++)
            for(int tmp=g[u];tmp;tmp=e[tmp].next)
                if(e[tmp].cut) deg[col[u]]++;
        for(int i=1;i<=sum;i++)
            if(deg[i]==1) ans++;
        printf("%d\n",(ans+1)/2);

给出完整实现:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 const int maxn=5005;
 5 const int maxm=20005;
 6 using namespace std;
 7 int n,m,cnt,deep,bridge,sum,top,ans;
 8 int g[maxn],dfn[maxn],low[maxn],col[maxn],vis[maxn],st[maxn],deg[maxn];
 9 struct Edge{int t,next;bool cut;}e[maxm];
10 void addedge(int u,int v)
11 {
12     e[++cnt].t=v;e[cnt].next=g[u];e[cnt].cut=0;
13     g[u]=cnt;
14 }
15 inline int change(int x)
16 {
17     if(x%2==0)
18         x--;
19     else
20         x++;
21     return x;
22 }
23 void tarjan(int u,int fa)
24 {
25     dfn[u]=++deep;low[u]=deep;
26     vis[u]=1;
27     st[++top]=u;
28     for(int tmp=g[u];tmp;tmp=e[tmp].next)
29     {
30         int v=e[tmp].t;if(v==fa) continue;
31         if(!dfn[v])
32         {
33             tarjan(v,u);
34             low[u]=min(low[u],low[v]);
35             if(low[v]>dfn[u])
36             {
37                 bridge++;
38                 e[tmp].cut=true;
39                 int tmp1=change(tmp);
40                 e[tmp1].cut=true;
41             }
42         }
43         else if(vis[v]) low[u]=min(low[u],dfn[v]);
44     }
45     if(dfn[u]==low[u])
46     {
47         col[u]=++sum;vis[u]=0;
48         while(st[top]!=u)
49         {
50             col[st[top]]=sum;
51             vis[st[top--]]=0;
52         }
53         top--;
54     }
55 }
56 void init()
57 {
58     cnt=deep=bridge=sum=top=ans=0;
59     memset(g,0,sizeof(g));
60     memset(dfn,0,sizeof(dfn));
61     memset(low,0,sizeof(low));
62     memset(vis,0,sizeof(vis));
63     memset(st,0,sizeof(st));
64     memset(deg,0,sizeof(deg));
65     memset(e,0,sizeof(e));
66 }
67 int main()
68 {
69     int u,v;
70     while(scanf("%d%d",&n,&m)==2)
71     {
72         init();
73         while(m--)
74         {
75             scanf("%d%d",&u,&v);
76             addedge(u,v),addedge(v,u);
77         }
78         tarjan(1,0);
79         for(u=1;u<=n;u++)
80             for(int tmp=g[u];tmp;tmp=e[tmp].next)
81                 if(e[tmp].cut) deg[col[u]]++;
82         for(int i=1;i<=sum;i++)
83             if(deg[i]==1) ans++;
84         printf("%d\n",(ans+1)/2);
85     }
86     return 0;
87 }

猜你喜欢

转载自www.cnblogs.com/aininot260/p/9431155.html
今日推荐