图论——有向图边连通分量(模板)

有向图边连通分量(模板)

题意:给定有向图,问最少加多少边能使得图变为边连通分量
解法:tarjan缩点之后,整个图变为一颗树,答案就是叶子节点(度数为1)的一半。

const int N=1e5;
int n,m;int h[N],e[N],ne[N],idx;int is_bridge[N]int timestamp,dfn[N],low[N],dcc_cnt,id[N];int d[N],stk[N],top;
//点数、边数、邻接表、是否为桥、时间戳、dfs序号、最小dfs祖先、有向图连通分量数量、代表每个点属于哪个连通分量、度数、栈
void add(int a,int b)
{e[idx]=b,ne[idx]=h[a],h[a]=idx++;}
void tarjan(int u, int from)
{
    dfn[u] = low[u] = ++ timestamp;  stk[ ++ top] = u;
    for (int i = h[u]; i!=-1; i = ne[i])
    {
        int j = e[i];
        if (!dfn[j])//j未遍历过
        {
            tarjan(j, i);//dfs(j)
            low[u] = min(low[u], low[j]);//用j更新u
            if (dfn[u] < low[j])//j到不了u   // 则x-y的边为桥,//正向边is_bridge[i] 反向边is_bridge[i ^ 1]都是桥
                is_bridge[i] = is_bridge[i ^ 1] = true;   // 判断反向边 
        }
        // j遍历过 且i不是反向边(即i不是指向u的父节点的边)   // 因为我们不能用u的父节点的时间戳更新u
        else if (i != (from ^ 1))
            low[u] = min(low[u], dfn[j]);
    }
    if (dfn[u] == low[u])
    {
        ++ dcc_cnt;  int y;
        do { y = stk[top -- ];  id[y] = dcc_cnt;  } while (y != u);
    }
}

int main()
{
    cin >> n >> m; memset(h, -1, sizeof h);
    while (m -- )
       {int a, b; cin >> a >> b; add(a, b), add(b, a);}
    tarjan(1, -1);//防止搜反向边 用一个from
    for (int i = 0; i < idx; i ++ )    //如果边i是桥 在其所连的出边的点j所在强连通分量的度+1
        if (is_bridge[i])  d[id[e[i]]] ++ ;
    int cnt = 0;
    for (int i = 1; i <= dcc_cnt; i ++ )
        if (d[i] == 1) cnt ++ ;
    cout << (cnt + 1) / 2 << endl;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值