• POJ 3352 Road Construction


    POJ_3352

        这个题目和问最少添加多少条有向边使图成为一个强连通分量的题目有些类似,只不过题目的模型换成了边双连通分量的模型,我们同样可以用tarjan算法来解决,只不过相对于强连通分量的tarjan代码有稍许不同。

    由于题目中说明了各个点之间都是连通的,因而我们就不用再考虑孤立的点或者边双连通块了,只需要把最后的边双连通分量染色,求出各个所谓的是leaf的边双连通分量的个数,最后的答案就是(leaves+1)/2

    所谓的“叶子”,就是针对一个边双连通分量来讲,它只和除自己之外的一个边双连通分量间存在一条无向边,那么这个边双连通分量就是所谓的“叶子”。至于最后的结果为什么是(leaves+1)/2,我们可以类比最少添加多少条边使图成为一个强连通分量的题目。如果图是由多个强连通分量组成的,那么我们最后需要分别求出出度、入度为0的强连通分量的个数,然后取较大值,原因是我们可以构造有向边将出度为0和入度0的强连通分量一一连接起来,对于剩下的出度或者入度为0的强连通分量,我们只要每个都用一条有向边连接到其他的强连通分量上就可以了。

    而这个题目由于是双连通分量,实际上是没有入度和出度之分的,如果我们认为规定了出度和入度,那么对于边双连通分量,我们可以将入度为0的和入度为0的连在一起,出度为0的和出度为0的连在一起,也可以入度为0的和出度为0的连在一起,那么最后结果就显然是(leaves+1)/2了。当然,这个题目之所以能这么想,原因在于题目中各个边双连通分量之间是连通的,如果存在孤立的点或边双连通分量,就要麻烦一些了。具体实现时只用一个数组dgr[]来记录出入度即可,最后统计dgr[]的值为2的元素的个数即可。

    此外,这个题目在对边双连通分量染色时与强连通分量不同,因为对于强连通分量的染色,最后所有点都可以出栈,所以所有强连通分量都会被染色,而对于边双连通分量染色,由于找到一个桥之后才会进行一次出栈操作,但桥的数量是要比双连通分量的数目少1,所以最后会有一个边双连通留在了栈中,因而我们要先将paint数组初始化为0num初始化为1,这样就相当于将最后一个边双连通分量染成了0。

    #include<stdio.h>
    #include
    <string.h>
    int first[1010],next[2010],v[2010],n,dgr[1010];
    int time,dfn[1010],low[1010],s[1010],top,paint[1010],num;
    void tarjan(int u,int fa)
    {
    int e;
    dfn[u]
    =low[u]=++time;
    s[top
    ++]=u;
    for(e=first[u];e!=-1;e=next[e])
    if(v[e]!=fa)
    {
    if(!dfn[v[e]])
    {
    tarjan(v[e],u);
    if(low[v[e]]<low[u])
    low[u]
    =low[v[e]];
    else if(low[v[e]]>dfn[u])
    {
    for(s[top]=-1;s[top]!=v[e];)
    paint[s[--top]]=num;
    num++;
    }
    }
    else if(dfn[v[e]]<low[u])
    low[u]
    =dfn[v[e]];
    }
    }
    int main()
    {
    int i,j,k,r,a,b,u,e,leaves;
    while(scanf("%d%d",&n,&r)==2)
    {
    memset(first,
    -1,sizeof(first));
    e
    =0;
    for(i=0;i<r;i++)
    {
    scanf(
    "%d%d",&a,&b);
    a
    --;
    b
    --;
    v[e]
    =b;
    next[e]
    =first[a];
    first[a]
    =e;
    e
    ++;
    v[e]
    =a;
    next[e]
    =first[b];
    first[b]
    =e;
    e
    ++;
    }
    memset(dfn,
    0,sizeof(dfn));
    memset(paint,
    0,sizeof(paint));
    time
    =top=0;
    num
    =1;
    for(i=0;i<n;i++)
    if(!dfn[i])
    tarjan(i,
    -1);
    memset(dgr,
    0,sizeof(dgr));
    for(u=0;u<n;u++)
    {
    for(e=first[u];e!=-1;e=next[e])
    if(paint[u]!=paint[v[e]])
    {
    dgr[paint[u]]
    ++;
    dgr[paint[v[e]]]
    ++;
    }
    }
    leaves
    =0;
    for(i=0;i<num;i++)
    if(dgr[i]==2)
    leaves
    ++;
    printf(
    "%d\n",(leaves+1)/2);
    }
    return 0;
    }

      

      

  • 相关阅读:
    借鉴文章记录
    三方框架
    常用第三方库记录
    ios block 类型
    ios runtime部分事例方法说明
    ios url网址相关问题解说
    mysql迁移数据库函数中的坑
    mysql的事务隔离级别
    MySQL数据库的默认隔离级别为什么是可重复读
    实时查看mysql连接数
  • 原文地址:https://www.cnblogs.com/staginner/p/2137416.html
Copyright © 2020-2023  润新知