hdu3394Railway 双连通分量

/*
    题目描述:给出一张无向图,分别求出图中满足如下两种条件的边的数量:
                    1、该边不在任何简单环当中;
                    2、该边在多个简单环当中;

    方法:不在任何简单环当中的,就是图中的桥,可以求出来;
              通过简单环联想到点双连通分量,找到某个双连通分量,若该双连通分量包含的边数超过该点双连通分量包含的点数,那么这个双
              连通分量中的每条边都是满足条件2的边;
              综上,用dfs求出桥的数量与上述双连通分量中的边数输出即可;
              可以套用求双连通分量的模板来求上述二者
              求桥:在满足割点条件时额外判断是不是桥,若是,桥的数量+1;
              求BCC中的边数:在dfs的过程中,需要从栈中取出该分量中的每一条边,每取出一条,边数+1,最后判断边数是否大于该分量中的
              点数,若是,将该分量边数的总和加到ans中,最终ans就是答案;
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<stack>
#define mem(a,x) memset(a,x,sizeof(a))
using namespace std;
const int maxn = 10000 + 5;
struct Edge
{
    int u,v;
};
int iscut[maxn],pre[maxn],bccno[maxn],dfs_clock,bcc_cnt,bridge,ans;
vector<int>G[maxn] , bcc[maxn];
stack<Edge>S;
int dfs(int u , int fa)
{
    int lowu = pre[u] = ++dfs_clock;
    int child = 0;
    for(int i = 0;i<G[u].size();i++){
         int v = G[u][i];
         Edge e = { u , v};
         if(!pre[v]){
            S.push(e);
            child++;
            int lowv = dfs(v , u);
            lowu = min(lowv , lowu);
            if(lowv >= pre[u]){
                if(lowv > pre[u])       ++bridge;           //判断是否是桥
                iscut[u] = 1;
                bcc_cnt++;  bcc[bcc_cnt].clear();
                int add = 0;                                      //add含义:当前BCC中的边数;
                for(;;){
                    Edge x = S.top();       S.pop();
                    ++add;                                         //去一条边,add就加一;
                    if(bccno[x.u]!=bcc_cnt){
                        bcc[bcc_cnt].push_back(x.u);
                        bccno[x.u] = bcc_cnt;
                    }
                    if(bccno[x.v]!=bcc_cnt){
                        bcc[bcc_cnt].push_back(x.v);
                        bccno[x.v] = bcc_cnt;
                    }
                    if(x.u==u &&x.v == v){
                        break;
                    }
                }
                if(add > bcc[bcc_cnt].size())       //该BCC中边数大于点数,ans加上add
                    ans += add;
            }
         }
         else if(pre[v]<pre[u] && v != fa){
            S.push(e);
            lowu = min(lowu , pre[v]);
         }
    }
    if(fa < 0 &&child == 1)    iscut[u] = 0;
    return lowu;
}
void find_bcc(int n)
{
    mem(pre,0);
    mem(bccno, 0);
    mem(iscut , 0);
    while(!S.empty())   S.pop();
    dfs_clock = bcc_cnt = 0;
    for(int i = 1;i<=n;i++)
        if(!pre[i])         dfs(i , -1);
}
int main()
{
    int n , m;
    while(scanf("%d %d",&n,&m)!=EOF){
        if(!n&&!m)
            break;
        bridge = 0;
        ans = 0;
        int x , y ;
        for(int i = 0 ;i<=n;i++)        G[i].clear();
        for(int i = 0;i<=n;i++)         bcc[i].clear();
        for(int i = 0;i<m;i++){
            scanf("%d %d",&x,&y);
            G[x+1].push_back(y+1);
            G[y+1].push_back(x+1);
        }
        find_bcc(n);
        printf("%d %d\n",bridge , ans);
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值