poj3177 Redundant Paths

poj3177 Redundant Paths

题目大意:给一个连通图,求最少加多少边使它变成一个点联通分量。

先找割边,然后把没有桥的点双连通分量缩成一个连通分量。

这些连通分量按原来的关系连在一起就是一颗树。

把树变成一个点双联图图需要加(叶节点数+1)/2个边。

问题是怎么求点双连通分量。

如果一个点的dfn=low,说明目前栈中的元素都需要弹出,成为一个点双连通分量。

把他们记录到结构体里面就好了

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <stack>
#define in(a) a=read()
#define MAXN 10010
#define REP(i,k,n)  for(int i=k;i<=n;i++)
using namespace std;
inline int read(){
    int x=0,f=1;
    char ch=getchar();
    for(;!isdigit(ch);ch=getchar())
        if(ch=='-')
            f=-1;
    for(;isdigit(ch);ch=getchar())
        x=x*10+ch-'0';
    return x*f;
}
int n,m,ans;
int total=0,head[MAXN],nxt[MAXN<<1],to[MAXN<<1];//ÁÚ½Ó±í 
int ind,dfn[MAXN],low[MAXN],vis[MAXN<<1];//¸î±ß 
int num,bel[MAXN];//Ëõµã 
int du[MAXN];
stack <int> S;
inline void adl(int a,int b){
    total++;
    to[total]=b;
    nxt[total]=head[a];
    head[a]=total;
    return ;
}
inline void tarjan(int u){
    S.push(u);
    dfn[u]=low[u]=++ind;
    for(int e=head[u];e;e=nxt[e]){
        if(e%2 && vis[e+1])  continue;
        if(!(e%2) && vis[e-1])  continue; 
        if(vis[e])  continue;
        vis[e]=1;
        if(!dfn[to[e]]){
            tarjan(to[e]);
            low[u]=min(low[u],low[to[e]]);
        }
        else  low[u]=min(low[u],dfn[to[e]]);
    }
    if(low[u]==dfn[u]){
        num++;
        int v;
        do{
            v=S.top();
            bel[v]=num;
            S.pop();
        }while(u!=v);
    }
    return ;
}
int main(){
    in(n),in(m);
    int a,b;
    REP(i,1,m)  in(a),in(b),adl(a,b),adl(b,a);
    tarjan(1);
    REP(u,1,n)
        for(int e=head[u];e;e=nxt[e])
            if(bel[u]!=bel[to[e]])
                du[bel[u]]++,du[bel[to[e]]]++;
    REP(i,1,num)  if((du[i]/2)==1)  ans++;
    cout<<(ans+1)/2;
    return 0;
}

 

posted @ 2019-01-05 13:30 Dijkstra·Liu 阅读( ...) 评论( ...) 编辑 收藏
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值