• POJ 3352 (边双连通分量)


    题目链接http://poj.org/problem?id=3352

    题目大意:一个连通图中,至少添加多少条边,使得删除任意一条边之后,图还是连通的。

    解题思路

    首先来看下边双连通分量的定义:

    如果任意两点至少存在两条“边不重复”的路径,那么说这个图是边双连通的。

    那么本题中,删除任意一条边,就可以看作是毁掉一条路径,那么只要至少还存在一条路径即可。

    也就是说,转化成,求加最少的边,使这个图边双连通。

    判断边双连通有两个方法:

    ①【局限性】编号借助low数组,如果low[u]!=low[v],就代表这两个点在不同双连通分量中。

    这种方法有些小Bug,主要Bug在邻接表这样的不支持重边的数据结构上,如果有重边存在,

    那么即使low[u]!=low[v],u和v也有可能存在于同一个连通分量中。

    所以推荐使用链式前向星。

    ②Tarjin时借助并查集,由于桥(删除之后图就不连通的边)不属于任何双连通分量,所以在Tarjin时,把不是桥的边的u,v并在一起,

    表示u,v在同一个双连通分量里,进行缩点。

    下面来看一条结论:

    若要使得任意一棵树,在增加若干条边后,变成一个双连通图,那么

    至少增加的边数 =( 这棵树总度数为1的结点数 + 1 )/ 2。

    这样,只需要统计一下缩点之后每个点的度数即可。

    ①方法low[u]中存的就是缩点编点,②方法并查集find之后也是缩点编号,degree[编号]记录度数。

    注意,由于本题是无向图,对于每条边,只需degree[low[u]]++即可,不用管v。否则每个点的degree要除以2.

    最后ans=(leaf+1)/2.

    #include "cstdio"
    #include "cstring"
    #include "iostream"
    using namespace std;
    #define maxn 2005
    struct Edge
    {
        int to,next;
    }e[maxn*2];int pre[maxn],dfs_clock,low[maxn],degree[maxn],head[maxn],tol;
    void addedge(int u,int v)
    {
        e[tol].to=v;
        e[tol].next=head[u];
        head[u]=tol++;
    }
    int dfs(int u,int fa)
    {
        int lowu=pre[u]=++dfs_clock;
        for(int i=head[u];i!=-1;i=e[i].next)
        {
            int v=e[i].to;
            if(!pre[v])
            {
                int lowv=dfs(v,u);
                lowu=min(lowu,lowv);
            }
            else if(pre[v]<pre[u]&&v!=fa) lowu=min(lowu,pre[v]);
        }
        return low[u]=lowu;
    }
    int main()
    {
        //freopen("in.txt","r",stdin);
        int n,m,u,v;
        while(~scanf("%d%d",&n,&m))
        {
            memset(head,-1,sizeof(head));
            memset(pre,0,sizeof(pre));
            memset(low,0,sizeof(low));
            memset(degree,0,sizeof(degree));
            dfs_clock=0;tol=0;
            for(int i=1;i<=m;i++)
            {
                scanf("%d%d",&u,&v);
                addedge(u,v);
                addedge(v,u);
            }
            for(int i=1;i<=n;i++) if(!pre[i]) dfs(i,-1);
            for(int u=1;u<=n;u++)
            {
                for(int i=head[u];i!=-1;i=e[i].next)
                {
                    int v=e[i].to;
                    if(low[u]!=low[v]) degree[low[u]]++;
                }
            }
            int leaf=0;
            for(int i=1;i<=n;i++)
                if(degree[i]==1) leaf++;
            printf("%d
    ",(leaf+1)/2);
        }
    }
  • 相关阅读:
    高精度求n的累加和
    软件测试简介
    实数加法
    洛古P1542
    css制作三角形 实心的和空心的(笔试常考,特此分享)!!!!
    关于http主要的状态码
    关于http和https的概念和区别
    JavaScript关于闭包的理解和实例
    关于css编写
    关于javascript中apply()和call()方法的区别
  • 原文地址:https://www.cnblogs.com/neopenx/p/4065220.html
Copyright © 2020-2023  润新知