HDU_3394 Railway 无向图求块

http://acm.hdu.edu.cn/showproblem.php?pid=3394

题意:给定一个有N个点,M条边的无向图,求有多少条边没有在环内,有多少条边在至少2个环内。

思路:双连通求块,对于每个块,分别求出顶点数,记为a , 和边数,记为b,当a < b ,即顶点数小于边数的时候,则可以证明,这时候连通区域内的边都是clash边,当a == b的时候,则可以得出此时刚好是一个环,每条边都只在正好两个环内,当a > b的时候,这时候连通分量是一棵树,(其实只有两个顶点的时候才可能出现这种情况),此时的边都是没有构成环的边。

反思:一开始错误地以为可以对图进行缩点,然后用上面的规则求,这种方法是错误的,错误的根源就在于:在缩在一起的点中,不满足上面的规则,即当a < b的时候,并不是所有的边都是clash边,

比如:  1 ------ 2 

               |          |

               ----3----

               |          |

              4-------5

在上面的图中,每对顶点之间都有只有2条简单路径,就可以缩点,图中有5个顶点,6条边,但是6条边都不会出现clash的情况。因此要是用缩点就会出错。导致这种错误产生的原因就在于,求缩点的连通图中,只是要求任意两个结点之间有2条路径就可以了,而不能保证点连通性。

代码:

#include<stdio.h>
#include<string.h>
const int MAXN = 10010 ;
const int MAXM = 100010 ;
int N ,M ;
struct Node{
    int num,next ;
}edge[MAXM*2] ;
int root[MAXN] ,e_cnt ;
void init(){
    memset(root,-1, sizeof(root));
    e_cnt = 0 ;
}
void add(int a ,int b){
    edge[e_cnt].num = b ;
    edge[e_cnt].next = root[a];
    root[a] = e_cnt ++ ;
}
int dfn[MAXN] , low[MAXN], stack[MAXN] ;
int idx , top ,cnt ;
int block[MAXN] ,res1,res2;
bool vis[MAXN] ;

void count(){
    int a = block[0] ;
    int b = 0 ;
    for(int i=1 ;i<=block[0];i++){
        int u = block[i] ;
        for(int j=root[u] ;j!=-1;j=edge[j].next){
            int v = edge[j].num ;
            if( vis[v] )   b++ ;
        }
    }
    b /= 2 ;
    if( a > b ){
        res1 += b ;
    }
    else if(a < b){
        res2 += b ;
    }
}
void tarjin(int x, int pre){
    low[x] = dfn[x] = ++idx ;
    stack[++top] = x ;
    for(int i=root[x] ;i!=-1;i=edge[i].next){
        int u = edge[i].num ;
        if(u == pre)    continue ;

        if( dfn[u] == -1){
            tarjin(u,x) ;
            if( low[u] < low[x])    low[x] = low[u] ;
            if( low[u] >= dfn[x] ){
                block[0] = 0 ;
                int v  ;
                memset(vis,0,sizeof(vis));
                do{
                    v = stack[top--] ;
                    vis[v] = 1 ;
                    block[ ++block[0] ] = v ;
                }while(u != v) ;
                block[ ++block[0] ] = x ;
                vis[x] = 1 ;
                count();
            }
        }
        else if( dfn[u] < low[x] )
            low[x] = dfn[u] ;
    }

}
void solve(){
    idx = top = cnt = 0 ;
    memset(dfn , -1,  sizeof(dfn));
    res1 = res2 = 0 ;
    for(int i=1;i<=N;i++){
        if( dfn[i] == -1 ){
            tarjin(i , -1);
        }
    }
    printf("%d %d\n",res1,res2);
}
int main(){
    int a, b ;
    while(scanf("%d%d",&N,&M) && (N+M)){
        init() ;
        for(int i=1;i<=M;i++){
            scanf("%d%d",&a,&b);
            a++ ;b ++ ;
            add(a,b); add(b,a);
        }
        solve() ;
    }
    return 0 ;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值