hdu 2767 Proving Equivalences

本文深入探讨了如何通过最小数量的边增连通强连通图,详细介绍了强连通缩点、DAG转换及Tarjan算法的应用,并提供了具体的代码实现。

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

这道题是问最少添加几条边使得整个图强连通。首先,我们知道强连通是一个等价的关系,那么我们可以将强连通分量缩成一个点,即这个强连通分量的编号,这个过程称为强连通缩点。缩点后图变成一个DAG(有向无环),只需统计出度和入度为0的点各有多少,然后取大。这很好理解,因为我们添加边是从一个出度为0的点连到入度为0的点,这样就可以使整个图强连通。具体实现用的是Tarjan的算法= =。

#include <stack>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 20010;
const int M = 50010;

stack <int> S;

int n,m;
int tot, head[N];
int in[N],out[N];
int dfs_clock, point, vis[N], pre[N], low[N], belong[N];

struct edge{
    int v,next;
}node[M];

void init(){
    while(!S.empty()) S.pop();
    tot = dfs_clock = point = 0;
    for(int i=1; i<=n; i++){
        head[i] = -1;
        vis[i] = 0;
        pre[i] = 0;
        out[i] = 0;
        in[i] = 0;
    }
}

void addedge(int u, int v){
    node[tot].v = v;
    node[tot].next = head[u];
    head[u] = tot++;
}

void Tarjan(int u){
    pre[u] = low[u] = ++dfs_clock;
    vis[u] = 1;
    S.push(u);
    for(int i=head[u]; i!=-1; i=node[i].next){
        int v = node[i].v;
        if(!pre[v]){
            Tarjan(v);
            low[u] = min(low[u], low[v]);
        }
        else if(vis[v] && pre[v] < low[u])
            low[u] = pre[v];
    }
    if(pre[u] == low[u]){
        point++;
        int x;
        do{
            x = S.top();
            vis[x] = 0;
            belong[x] = point;
            S.pop();
        }while(x != u);
    }
    return;
}

void solve(){
    for(int i=1; i<=n; i++)
        if(!pre[i]) Tarjan(i);
    if(point == 1){
        puts("0");
        return;
    }
    for(int u=1; u<=n; u++){
        for(int i=head[u]; i!=-1; i=node[i].next){
            int v = node[i].v;
            if(belong[u] != belong[v]){
                out[belong[u]]++;
                in[belong[v]]++;
            }
        }
    }
    int a = 0, b = 0;
    for(int i=1; i<=point; i++){
        if(!in[i]) a++;
        if(!out[i]) b++;
    }
    printf("%d\n",max(a, b));
}

int main(){
    int cas;
    scanf("%d",&cas);
    while(cas--){
        scanf("%d%d",&n,&m);
        init();
        for(int i=0; i<m; i++){
            int u, v;
            scanf("%d%d",&u,&v);
            addedge(u, v);
        }
        solve();
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值