图论——有向图强连通分量&无向图双连通分量

有向图强连通分量


tarjan 算法模板

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
const int N = 110, M = 10010;
int n;
int h[N], e[M], ne[M], idx;
int low[N], dfn[N], din[N], dout[N], timestamp, top;
int stk[N], id[N], dcc_cnt;
bool is_instk[N];

void add(int a, int b)
{
    e[idx] = b; ne[idx] = h[a]; h[a] = idx ++;
}

void tarjan(int u)
{
    low[u] = dfn[u] = ++timestamp;
    stk[++top] = u;
    is_instk[u] = true;
    for(int i = h[u]; ~i; i = ne[i])
    {
        int j = e[i];
        if(!dfn[j])
        {
            tarjan(j);
            low[u] = min(low[u], low[j]);
        }
        else if(is_instk[j])
        {
            low[u] = min(low[u], dfn[j]);
        }
    }
    if(low[u] == dfn[u])
    {
        dcc_cnt ++;
        int y;
        do{
            y = stk[top --];
            id[y] = dcc_cnt;
            is_instk[y] = false;
        }while(y != u);
    }
}

int main()
{
    int n; cin >> n;
    memset(h, -1, sizeof(h));
    for(int i = 1; i <= n; i ++)
    {
        int x;
        while(cin >> x, x)
        {
            add(i, x);
        }
    }
    for(int i = 1; i <= n; i ++)
    {
        if(!dfn[i])
        tarjan(i);
    }
 
    for(int u = 1; u <= n; u ++)
    {
        for(int i = h[u]; ~i; i = ne[i])
        {
            int j = e[i];
            if(id[j] != id[u])
            {
                dout[id[u]] ++;
                din[id[j]] ++;
            }
        }
    }
    
    int a = 0, b = 0;
    for(int i = 1; i <= dcc_cnt; i ++)
    {
        if(!din[i]) a++;
        if(!dout[i]) b ++;
    }
    cout << a << endl;
    if(dcc_cnt == 1) cout << 0 << endl;
    else cout << max(a, b) << endl;
    return 0;
}

结论:

  1. 有向图最少需要加min(in, out) 边变为强连通分量
    in是入度为0的连通分量的个数
    out是出度为0的连通分量的个数

无向图双连通分量

无向图边的双连通分量(桥)

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
const int N = 5100, M = 20100;
int n, m; 
int h[N], e[M], ne[M], idx;
int low[N], dfn[N], stk[N], d[N], id[N], top, timestamp, dcc_cnt;
bool is_bridge[M];

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; i = ne[i])
    {
        int j = e[i];
        if(!dfn[j])
        {
            tarjan(j, i);
            low[u] = min(low[u], low[j]);
            if(dfn[u] < low[j])
            {
                is_bridge[i] = is_bridge[i^1] = true;
            }
        }
        else if(i != (from ^ 1))
        {
            low[u] = min(low[u], dfn[j]);
        }
    }
    if(low[u] == dfn[u])
    {
        int y; ++dcc_cnt;
        do{
            y = stk[top--];
            id[y] = dcc_cnt;
        }while(y != u);
    }
}

int main()
{
    cin >> n >> m;
    memset(h, -1, sizeof(h));
    for(int i = 1; i <= m; i ++)
    {
        int a, b; cin >> a >> b;
        add(a, b), add(b, a);
    }
    tarjan(1, -1);
    for(int i = 0; i < idx; i ++)
    {
        if(is_bridge[i])
        {
            d[id[e[i]]] ++;
        }
    }
    int ans = 0;
    for(int i = 1; i <= dcc_cnt; i ++)
    {
        if(d[i] == 1)
        {
            ans ++;
        }
    }
    cout << (ans+1)/2 << endl;
    return 0;
}

结论

  1. 把无向图变为边双连通无向图最少加缩点后叶子节点除2上取整
    res = (ans+1)/2 叶子节点就是缩点后度数唯一的连通分量
相关推荐
东方翱翔5 分钟前
第十六届蓝桥杯大赛软件赛省赛第二场 C/C++ 大学 A 组
算法·职场和发展·蓝桥杯
Blossom.11833 分钟前
量子计算在密码学中的应用与挑战:重塑信息安全的未来
人工智能·深度学习·物联网·算法·密码学·量子计算·量子安全
1白天的黑夜138 分钟前
贪心算法-860.柠檬水找零-力扣(LeetCode)
c++·算法·leetcode·贪心算法
搏博1 小时前
专家系统的基本概念解析——基于《人工智能原理与方法》的深度拓展
人工智能·python·深度学习·算法·机器学习·概率论
yzx9910131 小时前
决策树随机深林
人工智能·python·算法·决策树·机器学习
Y1nhl1 小时前
力扣hot100_子串_python版本
开发语言·python·算法·leetcode·职场和发展
uhakadotcom1 小时前
过来人给1-3 年技术新人的几点小小的建议,帮助你提升职场竞争力
算法·面试·架构
wuqingshun3141592 小时前
蓝桥杯 16. 密文搜索
c++·算法·职场和发展·蓝桥杯·深度优先
Brookty2 小时前
【数据结构】哈希表
数据结构·算法·哈希算法·散列表
Dovis(誓平步青云)4 小时前
【数据结构】·励志大厂版(复习+刷题):二叉树
c语言·数据结构·经验分享·笔记·学习·算法·学习方法