冗余路径(无向图的边的双连通分量)

冗余路径:https://www.acwing.com/problem/content/397/

无向图的边的双连通分量:理解1:极大的不包含桥的连通块(桥(割边):去掉这条边,图会被分割成俩个不连通的子图);理解2:任何两个点之间必定包含俩条不相交的路径(边的位置不相交,点可以相交,如果俩条路径分别包含(x,y),(y,x)俩个边,那么是相交的路径)。

有向图的强连通分量:任何两个点都可以互相到达。

题意:给一个无向连通图,问最少加几条边可以让这个图变成边的双连通图

题解:有向图的强连通分量:最少加几条边,可以让这个图变成,强连通图,p是入度为0的点,q是出度为0的点。
无向图的边双连通分量:最少加几条边,可以让这个图变成,边的双连通图,cnt是叶子节点的数量,也就是度数为1的点的数量。
在这里插入图片描述

#include <algorithm>
#include <bitset>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>
#include <functional>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
//#include <unordered_map>
//#include <unordered_set>
//#include <bits/stdc++.h>
//#define int long long
#define pb push_back
#define pii pair<int, int>
#define mpr make_pair
#define ms(a, b) memset((a), (b), sizeof(a))
#define x first
#define y second
typedef long long ll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;

using namespace std;
inline int read() {
    char ch = getchar();
    int s = 0, w = 1;
    while (ch < '0' || ch > '9') {
        if (ch == '-') w = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        s = s * 10 + ch - '0', ch = getchar();
    }
    return s * w;
}
const int N = 5010, M = 20010;
int n, m;
int h[N], e[M], ne[M], idx;
int dfn[N], low[N], timestamp;
int stk[N], top;
int id[N], dcc_cnt;
bool is_bridge[M];
int d[N];
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]) {
                //边的编号是0 1是一对,2 3是一对 4 5是一对
                is_bridge[i] = is_bridge[i ^ 1] = true;
            }
        } 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);
    }
}
signed main() {
    n = read(), m = read();
    memset(h, -1, sizeof(h));
    while (m--) {
        int a = read(), b = read();
        add(a, b), add(b, a);
    }
    tarjan(1, -1);//将边的双连通分量找到,缩点
    for (int i = 0; i < idx; i++) {
        //枚举边的编号,看看是否是桥,是桥的话那就给它对应的两边的点所在的双连通分量度数+1
        //因为i是桥的话,i^1一定也是桥,也就是这条无向边的两端的点对应的连通分量中间连一条边
        if (is_bridge[i]) d[id[e[i]]]++;
    }
    int cnt = 0;
    //度数为1,代表这个连通分量是叶子节点,记录数量
    for (int i = 1; i <= dcc_cnt; i++) {
        if (d[i] == 1) cnt++;
    }
    printf("%d\n", (cnt + 1) / 2);
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值