[Poj]3177——双联通分量

[题目大意]
  • 给定一张图,求最少加入几条边可以使得整个图成为一个边双连通图。

[分析题解]
  •  经典的老题目了,学双联通分量必做。前一段时间做了一次,结果不断的WA-_-一怒之下扔掉,今天又回来做,详细的修改了程序框架之后重写很快就过了。
  • 具体的说也是非常简单:求双联通分量,染色,缩点,然后求度为1的点数,答案就是(这个数+1) Div 2。
  • 因为有重边,以前不太明确怎么处理,直到看了Guo神牛的题解之后,恍然大悟,只需要在Dfs的同时,记下到这个边的这条边的反向边的编号,到时候不要再从这个走回去,但是可以从重边走回去。限制边,而不是限制点,这样处理就搞定了重边的情况。
  • 其他的就比较简单了,按照Low和Dfn的定义,如果某个点拓展完后Low=Dfn,那么栈中从栈顶到这个店为止的部分都属于同一个双联通分量。这个由Dfs树的性质决定的。这样求出来之后,只需要对这些点进行染色,不需要真的缩点,只要再去重新枚举原图中的边,向新的颜色不同的顶点之间加边即可。又由于双联通分量的定义,这样新的图一定是一个树,如果不是一个树,那有环的部分按照定义是应该在同一个双联通分量中的。然后求求度数,计算输出即可。

[个人代码]

fayaa.com/code/view/26770/


[相关链接] 
  1. http://www.byvoid.com/blog/biconnect/zh-hans/
  2. http://www.byvoid.com/blog/pku-3177/ 
  3. http://www.cnblogs.com/ACystalMoon/articles/2381205.html

  4.  

    http://www.cnblogs.com/rainydays/archive/2011/07/06/2099524.html

     


[启发总结]
  1.  特别是第四个!以前我了解了Low的定义,但是却没有发现这个区别:我们平时使用的Low,都是其子孙通过一条返祖边直接到达的点来决定的。如果我们把这个限制取消掉,按照其提供的做法,可以很简单的对双联通分量进行染色。
    http://www.cnblogs.com/rainydays/archive/2011/07/06/2099524.html
    poj3177

    求添加多少条边可变连通图。
    接着poj3352的看。对于这种题,我们正常的做法是求桥,删桥,求连通分支,缩点,构建新图,求叶子数。
    我们有一种简便方法。需要对tarjan算法做一些变化。我们之前规定low[u]是其子孙通过一条返祖边直接到达的点,把这个改成是其子孙可以连续通过多条返祖边所能到达的点。那么low[u]=min(low[v],dfn[u]);
    这样做的缺陷是,不能求割点了,多次返祖会导致求割点的错误,在多环两两以单个点相连排成一条线,且每两个连接点间只有一条边的情况中,那些连接点本应是割点,但是在dfs过程中,这些连接点之间的边又恰好不是树枝边的话,low[u]可能会通过多次返祖,从一个割点不断的经过这些割点到达最上边的割点才记录下low[u]。
    这样中间的割点就都不符合dfn(u)<=low[v]了。
    但是这样做有一个好处,就是所有的对于边的双连通分支都以low标记出来了,即属于同一双连通分支的所有点的low都等于同一个值。因为在不遇到桥的情况下,low可以返祖到该连同分支在遍历树中的最高点(dfn最小的点)。
    这样就相当于整理出了所有的对于边的双连通分支。我们直接遍历所有的边,观察边的两端点是否属于同一分支,若不属于则修改两点的度数。然后看有多少个度数为1的点即可。
    这题有重边,注意判断。
    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    using namespace std;

    #define maxn 5005
    #define maxm 10005

    struct Edge
    {
        int v, next;
    } edge[maxm];

    int n, m;
    int head[maxn];
    bool hash[maxn][maxn];
    int ecount, tcount;
    int dfn[maxn], vis[maxn], low[maxn], degree[maxn];

    void addedge(int a, int b)
    {
        edge[ecount].v = b;
        edge[ecount].next = head[a];
        head[a] = ecount;
        hash[a][b] = hash[b][a] = true;
        ecount++;
    }

    void input()
    {
        memset(head, -1sizeof(head));
        ecount = 0;
        scanf("%d%d", &n, &m);
        for (int i = 0; i < m; i++)
        {
            int a, b;
            scanf("%d%d", &a, &b);
            a--;
            b--;
            if (hash[a][b])
                continue;
            addedge(a, b);
            addedge(b, a);
        }
    }

    void dfs(int fa, int u)
    {
        vis[u] = true;
        low[u] = dfn[u] = tcount++;
        for (int i = head[u]; i != -1; i = edge[i].next)
        {
            int v = edge[i].v;
            if (v == fa)
                continue;
            if (!vis[v])
                dfs(u, v);
            low[u] = min(low[u], low[v]);
        }
    }

    int tarjan()
    {
        memset(dfn, 0sizeof(dfn));
        memset(vis, 0sizeof(vis));
        memset(degree, 0sizeof(degree));
        tcount = 0;
        dfs(00);
        int ret = 0;
        for (int i = 0; i < n; i++)
            for (int j = head[i]; j != -1; j = edge[j].next)
            {
                int v = edge[j].v;
                if (low[i] != low[v])
                    degree[low[i]]++;
            }
        for (int i = 0; i < n; i++)
            if (degree[i] == 1)
                ret++;
        return (ret + 1) / 2;
    }

    int main()
    {
        //freopen("t.txt", "r", stdin);
        input();
        int ans = tarjan();
        printf("%d\n", ans);
        return 0;
    }
posted @ 2012-06-07 21:44  PerSeAwe  阅读(505)  评论(1编辑  收藏  举报