HDU 2767 Proving Equivalences Tarjan 强连通缩点

本文介绍了一种求解最少添加边数使图变为强连通的算法。通过Tarjan算法处理图的强连通分量,并将其缩点转换为有向无环图。最终通过计算出入度为0的点数确定最小边数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2767

题意:问一个图至少加多少条边后能成为强连通的
我们用Tarjan处理完图的强连通分量以后,就将每一个强连通分量缩成一个点,新的图之后就成为一个有向无环图
让这个有向无环图成为强连通的方法就是加上入度为0的点的个数 和 出度为0的点的个数取max

现在来看看怎么连这个有向无环图 是使这个图强连通
对于一个出度为0 的点u 我们添加一个边(u,v) v为入度为0的点 然后更新u,v的出入度
最后到没有出度为0的点 或者 入度为0的点 那么剩下的点可以随便连图上任何一个点
代码:

#include <bits/stdc++.h>
#define sf scanf
#define pf printf

using namespace std;
const int maxn = 20000 + 50;
vector<int> Adj[maxn];
stack<int> Stack;
int low[maxn],dfn[maxn],inStack[maxn],belong[maxn],ins[maxn],ous[maxn];
int TOT,Block_CNT;
void DFS(int u){
    dfn[u] = low[u] = TOT++;
    Stack.push(u);inStack[u] = 1;
    int len = Adj[u].size(),v;
    for(int i = 0;i < len;++i){
        v = Adj[u][i];
        if(dfn[v] == -1){
            DFS(v);
            low[u] = min(low[u],low[v]);
        }
        else if(inStack[v]) low[u] = min(low[u],dfn[v]);
    }

    if(low[u] == dfn[u]){
        int tmp;
        do{
            tmp = Stack.top();Stack.pop();
            inStack[tmp] = false;
            belong[tmp] = Block_CNT;
        }while(tmp != u);
        Block_CNT++;
    }
}

int main(){
    int T;sf("%d",&T);
    while(T--){
        memset(dfn,-1,sizeof dfn);
        memset(inStack,0,sizeof inStack);
        memset(ins,0,sizeof ins);
        memset(ous,0,sizeof ous);
        TOT = 1;Block_CNT = 0;
        int n,m;sf("%d %d",&n,&m);
        for(int i = 1;i <= n;++i) Adj[i].clear();
        for(int i = 0;i < m;++i){
            int u,v;sf("%d %d",&u,&v);
            Adj[u].push_back(v);
        }
        for(int i = 1;i <= n;++i)
            if(dfn[i] == -1) DFS(i);
        for(int i = 1;i <= n;++i){

            int len = Adj[i].size();
            for(int j = 0;j < len;++j){
                int v = Adj[i][j];
                if(belong[i] != belong[v]) ous[belong[i]]++,ins[belong[v]]++;
            }
        }
        int cnt_ous = 0,cnt_ins = 0;
        for(int i = 0;i < Block_CNT;++i){
            if(ins[i] == 0) cnt_ins++;
            if(ous[i] == 0) cnt_ous++;
        }
        if(Block_CNT == 1) cnt_ous = 0,cnt_ins = 0;
        pf("%d\n",max(cnt_ous,cnt_ins));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值