hdu 3394 Railway

双连通分量

题意:给一个无向图。如果至少有两个环共用了一些边,那么这些边被认为是“冲突边”。如果一些边不在任何一个环中,这些边被认为是“多余边”。你要找出这个图中有多少“多余边”和“冲突边”然后输出条数。另外这图不一定是连通的

 

1.“多余边”不在任何一个环中,那么多余边一定是桥,所以统计这个无向图中有多少桥即可

2.“冲突边”有多少,这个有点费劲,但是不难想到。如果一个环比较特殊,n个点刚好n条边,例如(1,2)(2,3)(1,3)这种环,这个环内,一条“冲突边”都没有,但是如果一个环内的边数大于点数,那么这个环内所有边都是“冲突边”(真可惜,因为有多出来的那些边后,相当于把最外面的大环分割成了内部的几个小环,这些小环和小环之间,小环和大环之间一定会公用一些边,这些边就是“冲突边”,而且可以发现,所有边都会被公用,太可惜了......),例如sample里面的(5,6)(5,4)(6,7)(4,7)(5,7),相当于最外面的大环<6,5,4,7,6> , 而里面的边(5,7)把这个大环分割成了两个小环

 

所以做法就是,求出这个无向图有多少个点双连通分量,对于每个点双连通分量,如果内部的边数>点数,那么这些边全部都是冲突边

 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <stack>
using namespace std;
#define N 10010
#define M 100010

int n,tot;
int head[N];
struct edge
{
    int u,v,next;
}e[2*M];
int dfn[N],low[N],ins[N],inbcc[N],dcnt,bcnt;
stack<int>sta;
vector<int>bcc;
int res , __res;

void add(int u ,int v ,int k)
{
    e[k].u = u; e[k].v = v;
    e[k].next = head[u]; head[u] = k++;
    u = u^v; v = u^v; u = u^v;
    e[k].u = u; e[k].v = v;
    e[k].next = head[u]; head[u] = k++;
}

void func()
{
    memset(inbcc , 0, sizeof(inbcc));
    for(int i=0; i<bcc.size(); i++)
        inbcc[bcc[i]] = 1; //标记该点在当前这个点连通分量内
    int count = 0;
    for(int i=0 ; i<bcc.size(); i++)
    {
        int u = bcc[i];
        for(int k=head[u]; k!=-1; k=e[k].next)
        {
            int v = e[k].v;
            if(inbcc[v]) count++; //统计这个点连通分量内有多少条边 
        }
    }
    count /= 2; //统计的是有向边,要除2变为无向边
    if(count > bcc.size()) //如果这个点连通分量内的边数大于点数,那么每条边都是“冲突”的
        __res += count;
}

void dfs(int u ,int fa)
{
    dfn[u] = low[u] = ++dcnt;
    sta.push(u); ins[u] = 1;
    for(int k=head[u]; k!=-1; k=e[k].next)
    {
        int v = e[k].v;
        if(v == fa) continue;
        if(!dfn[v]) //树边
        {
            dfs(v,u);
            low[u] = min(low[u] , low[v]);
            if(low[v] > dfn[u]) //找到一个桥
                res++;
            if(low[v] >= dfn[u]) //点u是割点,并且找到了一个点双连通分量
            {
                bcc.clear(); //先清空容器
                while(true)
                {
                    int x = sta.top();
                    sta.pop(); ins[x] = 0;
                    bcc.push_back(x); //装入容器中
                    if(x == v) break;
                }
                bcc.push_back(u); //点u也属于这个连通分量,但是不能出栈因为还可能属于别的连通分量
                func(); //去处理这个点连通分量
            }
        }
        else if(ins[v]) //后向边
            low[u] = min(low[u] , dfn[v]);
    }
}

void solve()
{
    dcnt = bcnt = res = __res = 0;
    while(!sta.empty()) sta.pop();
    memset(ins,0,sizeof(ins));
    memset(dfn,0,sizeof(dfn));

    for(int i=0; i<n; i++)
        if(!dfn[i])
        {
            dfs(i,-1);
        }
    cout << res << " " << __res << endl;
}

int main()
{
    while(cin >> n >> tot)
    {
        if(!n && !tot) break;
        tot *= 2;
        memset(head,-1,sizeof(head));
        for(int i=0; i<tot; i+=2)
        {
            int u,v;
            cin >> u >> v;
            add(u,v,i);
        }
        solve();
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/scau20110726/archive/2013/05/19/3087815.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值