hdu3394——Railway——————【判断桥和双连通分量】

/**

   解题思路:

                  本题中的冲突边是指形成环的边且边数大于点数,即本环中所有的边都是冲突边。

                                 不必需边是无向边形成的环,即所要求的桥。

*/

题目大意:要在景点修铁路,给定景点个数n,要修的铁路条数m。如果是不是无向边构成的环形路线,则是不必要的铁路。如果两个环有公共边,则环中的所有边都是冲突边。,问不必要的铁路条数,和冲突边铁路有多少条。

/**
    一个没有(关节点)割顶的连通图称为重连通图。
    点-双连通分量性质:
            不同双连通分量最多只有一个公共点,且它一定是割顶。
            任意割顶都是至少两个不同双连通分量的公共点。
    边-双连通分量性质:
            除了桥不属于任何边-双连通分量之外,其他每条边恰好
        属于一个边-双连通分量。
            把所有桥删除之后,每个连通分量对应原图中的一个边-双
        连通分量。
*/

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<vector>
#include<stack>
#include<algorithm>
using namespace std;
const int MAXV=10010;
int pre[MAXV],iscut_v[MAXV],bccno[MAXV];
int dfs_clock,bcc_cnt,bri_num,no_need_num;
vector<int>G[MAXV],bcc[MAXV];
struct Edge{

    int u,v;
};
int n,m;
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=(Edge){u,v};
        if(!pre[v]){                    //未访问过的点

            S.push(e);                  //存放所有边的栈
            child++;
            int lowv=dfs(v,u);          //计算v的low
            lowu=min(lowu,lowv);        //用后代的low更新自己
            if(lowv>pre[u]){            //桥的判断

                bri_num++;
            }
            if(lowv>=pre[u]){           //割顶的判断,u为割顶

                iscut_v[u]=true;
                bcc_cnt++;              //计算双连通分量的数量
                bcc[bcc_cnt].clear();   //清空要加入的那行vector的值
                int tmp_edg=0;          //计算连通分量中的边数
                for(;;){

                    Edge x= S.top();
                    S.pop();            //退栈
                    tmp_edg++;
                    if(bccno[x.u]!=bcc_cnt){
                                        //如果该点在这个连通分量中
                        bcc[bcc_cnt].push_back(x.u);    //将该点加入该连通分量的vector
                        bccno[x.u]=bcc_cnt;             //标记在哪个连通分量中
                    }
                    if(bccno[x.v]!=bcc_cnt){            //同理记录该边的另一端

                        bcc[bcc_cnt].push_back(x.v);
                        bccno[x.v]=bcc_cnt;
                    }       //如果将这个分量中的边都退栈了,跳出,不影响u在其他分量中存在
                    if(x.u==u&&x.v==v){break; }
                }
                int tmp_ver=0;                          //记录分量中的顶点个数
                for(int i=0;i<n;i++){

                    if(bccno[i]==bcc_cnt){

                        tmp_ver++;                      //同属于一个分量,自加
                    }
                }
                if(tmp_ver<tmp_edg){  //如果分量中边大于顶点个数,各个边都会互相影响

                    no_need_num+=tmp_edg;
                }
            }
        }                               //pre[v]<pre[u]就是限制仍然有一条边连向已经访问过v再次入栈
        else if(pre[v]<pre[u]&&v!=fa){     //如果是反向边
            S.push(e);
            lowu=min(lowu,pre[v]);                      //用反向边更新自己
        }
    }
    if(fa<0&&child==1)                                  //如果是根结点,且儿子只有一个
        iscut_v[u]=0;                                   //则不是割顶
    return lowu;
}
void find_bcc(int n){

    memset(pre,0,sizeof(pre));
    memset(iscut_v,0,sizeof(iscut_v));
    memset(bccno,0,sizeof(bccno));
    dfs_clock=bcc_cnt=bri_num=no_need_num=0;
    for(int i=0;i<n;i++){                               //图可能不是连通图

        if(!pre[i])
            dfs(i,-1);
    }
}
int main(){

    while(scanf("%d%d",&n,&m)!=EOF&&(n+m)){

        for(int i=0;i<MAXV;i++){

            G[i].clear();
        }
        for(int i=0;i<MAXV;i++){

            bcc[i].clear();
        }
        for(int i=0;i<m;i++){

            int a,b;
            scanf("%d%d",&a,&b);
            G[a].push_back(b);
            G[b].push_back(a);
        }
        find_bcc(n);
        printf("%d %d\n",bri_num,no_need_num);

    }
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值