POJ 3352 Road Construction 构造双连通分量 && POJ 3177Redundant Paths注意重边

       无向图的Tarjan和有向图求强连通分量的Tarjan很像...注意几个不同...

    1、没有栈..所以判断时先是看点有没有访问过...else的时候就直接更新Low...不需要再来个判断instack....

       2、DFS时不能从 a 递归到了 b..b又马上从a来更新...所以要多加一个notpre..代表递归b时是从哪个点进去的...防止这种情况..

     3、Low相等的点在无向图中就是在一个双连通图中...这个比有向图的方便..有向图还需要用栈来维护..通过判断退栈来判断强连通分量..

 备注:第三条是一个环的情况 如果多个环就是错的,做HDU 3394。。。所以用low是否相等来判断是否在一个连通分量中是有问题的

 

大神的blog   http://www.byvoid.com/blog/biconnect/

     http://blog.csdn.net/kk303/article/details/6881034

 

http://poj.org/problem?id=3352

题意:加一些边构造双连通分量,图本来就是连通的

把low相等的缩成一个点,最后形成一棵树  left为 度为1的点  则 (left+1)/2为答案

做的真纠结 RE了一晚上,因为输入的时候漏了while(scanf()) 应为while(~scanf())

贴个代码,睡觉去

代码(边的双连通分量):

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#define Min(a,b)a<b?a:b
#define nMAX 1005
#define mMAX 2010
using namespace std;
int head[nMAX],dfn[nMAX],low[nMAX],out[nMAX],belon[nMAX],stack[nMAX];
int s_edge,times,n,atype,top;
struct Edge
{
    int u,v,nxt;
}edge[mMAX];
void addedge(int u,int v)
{
    edge[++s_edge].v=v;
    edge[s_edge].nxt=head[u];
    head[u]=s_edge;

    edge[++s_edge].v=u;
    edge[s_edge].nxt=head[v];
    head[v]=s_edge;
}
void tarjan(int u,int fa)
{
    stack[++top]=u;
    dfn[u]=low[u]=++times;
    for(int e=head[u];e;e=edge[e].nxt)
    {
        int v=edge[e].v;
        if(!dfn[v])
        {
            tarjan(v,u);
            low[u]=Min(low[u],low[v]);
            if(dfn[u]<low[v])//边的双连通
            {
                atype++;
                int j;
                do
                {
                    j=stack[top--];
                    belon[j]=atype;
                }while(j!=v);

            }
        }
        else if(v!=fa)low[u]=Min(low[u],dfn[v]);
    }
}
void init()
{
    memset(head,0,sizeof(head));
    memset(dfn,0,sizeof(dfn));
    memset(out,0,sizeof(out));
    times=0;
    s_edge=0;
    atype=0;
    top=0;
}
int main()
{
    int CASE=0,i,j,m;
    while(~scanf("%d%d",&n,&m))
    {
        CASE++;
        init();
        while(m--)
        {
            scanf("%d%d",&i,&j);
            addedge(i,j);
        }
        tarjan(1,-1);
        for(i=1;i<=n;i++)//缩点
        {
            for(int e=head[i];e;e=edge[e].nxt)
            {
                int v=edge[e].v;
                if(belon[v]!=belon[i])out[belon[i]]++;
            }
        }
        int ans=0;
        for(i=0;i<=atype;i++)//从0开始,因为根节点belon=0!!!
        if(out[i]==1)ans++;
        ans=(ans+1)/2;
        printf("%d\n",ans);
    }
    return 0;
}

  下面这个是一样的,只是求边的双连通分量时,有点不同

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#define Min(a,b)a<b?a:b
#define nMAX 1005
#define mMAX 2010
using namespace std;
int head[nMAX],dfn[nMAX],low[nMAX],out[nMAX],belon[nMAX],stack[nMAX];
int s_edge,times,n,atype,top;
struct Edge
{
    int u,v,nxt;
}edge[mMAX];
void addedge(int u,int v)
{
    edge[++s_edge].v=v;
    edge[s_edge].nxt=head[u];
    head[u]=s_edge;

    edge[++s_edge].v=u;
    edge[s_edge].nxt=head[v];
    head[v]=s_edge;
}
void tarjan(int u,int fa)
{
    stack[++top]=u;
    dfn[u]=low[u]=++times;
    for(int e=head[u];e;e=edge[e].nxt)
    {
        int v=edge[e].v;
        if(!dfn[v])
        {
            tarjan(v,u);
            low[u]=Min(low[u],low[v]);
        }
        else if(v!=fa)low[u]=Min(low[u],dfn[v]);
    }
    if(dfn[u]==low[u])//边的双连通
    {
        atype++;
        int j;
        do
        {
            j=stack[top--];
            belon[j]=atype;
        }while(j!=u);

    }
}
void init()
{
    memset(head,0,sizeof(head));
    memset(dfn,0,sizeof(dfn));
    memset(out,0,sizeof(out));
    times=0;
    s_edge=0;
    atype=0;
    top=0;
}
int main()
{
    int CASE=0,i,j,m;
    while(~scanf("%d%d",&n,&m))
    {
        CASE++;
        init();
        while(m--)
        {
            scanf("%d%d",&i,&j);
            addedge(i,j);
        }
        tarjan(1,-1);
        for(i=1;i<=n;i++)//缩点
        {
            for(int e=head[i];e;e=edge[e].nxt)
            {
                int v=edge[e].v;
                if(belon[v]!=belon[i])out[belon[i]]++;
            }
        }
        int ans=0;
        for(i=1;i<=atype;i++)
        if(out[i]==1)ans++;
        ans=(ans+1)/2;
        printf("%d\n",ans);
    }
    return 0;
}

  

  

Poj 3177 构造无向双连通图

http://poj.org/problem?id=3177

直接把poj 3352的代码改了改WA

原来是这个题会有重边

bool map[5000][5000]来判重,如果map[i][j]==1&&map[j][i]==1则有重边

改进,把map[][]去掉 判重 具体代码

代码:

#include<iostream>
 #include<cstdio>
 #include<cstring>
 #include<string>
 #define Min(a,b)a<b?a:b
 #define nMAX 5005
 #define mMAX 20005
 using namespace std;
 int head[nMAX],dfn[nMAX],low[nMAX],out[nMAX],stack[nMAX],belon[nMAX];
 int s_edge,times,n,atype,top;

 struct Edge
 {
     int u,v,nxt;
 }edge[mMAX];
 void addedge(int u,int v)
 {
     edge[++s_edge].v=v;
     edge[s_edge].nxt=head[u];
     head[u]=s_edge;

     edge[++s_edge].v=u;
     edge[s_edge].nxt=head[v];
     head[v]=s_edge;
 }
 void tarjan(int u,int fa)
 {
     //fg声明放外面就错,为什么呢??
     bool fg=1;
     stack[++top]=u;
     dfn[u]=low[u]=++times;
     for(int e=head[u];e;e=edge[e].nxt)
     {
         int v=edge[e].v;
         if(v==fa&&fg)//判重!!!
         {
             fg=0;
             continue;
         }

         if(!dfn[v])
         {
             tarjan(v,u);
             low[u]=Min(low[u],low[v]);
             if(dfn[u]<low[v])//边的双连通
            {
                atype++;
                int j;
                do
                {
                    j=stack[top--];
                    belon[j]=atype;
                }while(j!=v);

            }
         }
         else //if(v!=fa) 有重边的情况,所以去掉这里!!!
              low[u]=Min(low[u],dfn[v]);
     }
 }
 void init()
 {
     memset(head,0,sizeof(head));
     memset(dfn,0,sizeof(dfn));
     memset(out,0,sizeof(out));
     times=0;
     s_edge=0;
     top=0;
     atype=0;
 }
 int main()
 {
     int i,j,m;
     while(~scanf("%d%d",&n,&m))
     {
         init();
         while(m--)
         {
             scanf("%d%d",&i,&j);
             addedge(i,j);
         }
         tarjan(1,-1);
        for(i=1;i<=n;i++)//缩点
        {
            for(int e=head[i];e;e=edge[e].nxt)
            {
                int v=edge[e].v;
                if(belon[v]!=belon[i]){out[belon[i]]++;}
            }
        }
         int ans=0;
         for(i=0;i<=atype;i++)
         if(out[i]==1)ans++;
         ans=(ans+1)/2;
         printf("%d\n",ans);
     }
     return 0;
 }

  

 

  

转载于:https://www.cnblogs.com/sdau10kuaile/archive/2012/04/11/2441658.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值