割点、割边 模版——图论

割点模版

讲解传送门1

讲解传送门2

讲解传送门3

讲解传送门4

割点模版题

#include<bits/stdc++.h>
using namespace std;

const int mxn = 2e5 + 10;
vector<int> e[mxn];
vector<int> cut;
int low[mxn], dfn[mxn], idx;

void Tarjan(int u, int root)
{
    low[u] = dfn[u] = ++ idx;
    int child = 0;
    for(int i = 0; i < e[u].size(); i ++)
    {
        int v = e[u][i];
        if(! dfn[v]) 
        {
            Tarjan(v, root);
            low[u] = min(low[u], low[v]);
            if (u == root) child++;
            else if (low[v] >= dfn[u]) cut.push_back(u);
        }
        else low[u] = min(low[u], dfn[v]);
    }
    if(u == root && child >= 2) cut.push_back(root);
}

void init(int n)
{
    idx = 0;
    for(int i = 1; i <= n; i ++) e[i].clear(), dfn[i] = 0;
    cut.clear();
}

int main()
{
    int n, m;
    scanf("%d %d", &n, &m);
    init(n);
    int u, v;
    for(int i = 1; i <= m; i ++)
    {
        scanf("%d %d", &u, &v);
        e[u].push_back(v);
        e[v].push_back(u);
    }
    for(int i = 1; i <= n; i ++)
        if(! dfn[i]) Tarjan(i, i);

    sort(cut.begin(), cut.end());
    int size = unique(cut.begin(), cut.end()) - cut.begin();        //⚠️ 我们可能将某个 割点 重复的 放入cut 容器,所以我们要去重
    printf("%d\n", size);                                           //元素去重之后, 的输出的 下标范围也要改变
    for(int i = 0; i < size; i ++)
        printf("%d ", cut[i]);
    printf("\n");

    return 0;
}

割边模版

#include<iostream>
#include<vector>
using namespace std;

const int mxn = 1e5;
vector<int> e[mxn];
int low[mxn], dfn[mxn], idx;
vector<pair<int, int> > cut;

void Tarjan(int u, int p)
{
    low[u] = dfn[u] = ++ idx; 
    for(int i = 0; i < e[u].size(); i ++)
    {
        int v = e[u][i];
        if(p == v) continue;
        if(! dfn[v]) 
        {
            Tarjan(v, u);
            low[u] = min(low[u], low[v]);
            if(low[v] > dfn[u]) cut.push_back(make_pair(u, v));
        }
        else
            low[u] = min(low[u], dfn[v]);
    }
}

void init(int n)
{
    for(int i = 1; i <= n; i ++) e[i].clear(), dfn[i] = 0;
    cut.clear();
}

int main()
{
    int n, m;
    scanf("%d %d", &n, &m);
    init(n);
    for(int i = 1, u, v; i <= m; i ++)
    {
        scanf("%d %d", &u, &v);
        e[u].push_back(v);
        e[v].push_back(u);
    }
    for(int i = 1; i <= n; i ++) if(! dfn[i]) Tarjan(i, 0);

    for(auto x : cut)
        printf("%d %d\n", x.first, x.second);

    return 0;
}

同时求割点、割边代码

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <string>
#include <queue>
#include <map>
#include <unordered_map>
#include <bitset>
#include <vector>
#include <stack>
void fre() { system("clear"), freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { system("clear"), freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define ll long long
#define ull unsigned long long
#define db double
#define Pir pair<int, int>
#define m_p make_pair
#define INF 0x3f3f3f3f
#define esp 1e-7
#define mod (ll)(1e9 + 7)
#define for_(i, s, e) for(int i = (ll)(s); i <= (ll)(e); i ++)
#define rep_(i, e, s) for(int i = (ll)(e); i >= (ll)(s); i --)
#define sc scanf
#define pr printf
#define sd(a) scanf("%d", &a)
#define ss(a) scanf("%s", a)
using namespace std;

const int mxn = 1e5 + 10;
vector<int> e[mxn];
int low[mxn], dnf[mxn], idx;
int dot[mxn];                   //存储割点
int f[mxn];
map<Pir, int> edge;             //存储割边

void Tarjan(int u, int p, int root)
{
    int child = 0;
    low[u] = dnf[u] = ++ idx;
    for_(i, 0, e[u].size() - 1)
    {
        int v = e[u][i];
        if(v == p) continue;
        if(! dnf[v])
        {
            Tarjan(v, u, root);
            low[u] = min(low[u], low[v]);
            if(u == root) child ++;
            else if(low[v] >= dnf[u])
                dot[u] = 1;
            if(low[v] > dnf[u])
            {
                int x = min(u, v);
                int y = max(u, v);
                edge[m_p(x, y)] = 1;
            }
        }
        else low[u] = min(low[u], dnf[v]);
    }
    if(u == root && child >= 2) dot[root] = 1;
}

void init(int n)
{
    idx = 0;
    for_(i, 1, n) e[i].clear(), dnf[i] = dot[i] = 0;
    edge.clear();
}

int main()
{
    fre();
    int n, m;
    while(~ sc("%d %d", &n, &m))
    {
        int u, v;
        for_(i, 1, m)
        {
            sc("%d %d", &u, &v);
            e[u].pb(v);
            e[v].pb(u);
        }

        for_(i, 1, n) if(! dnf[i]) Tarjan(i, 0, i);

        for(auto [x, y] : edge)
        {
            cout << x.fi << " " << x.se << endl;
        }
        for_(i, 1, n)
            if(dot[i]) cout << i << " ";

    }

    return 0;
}


另外一种同时求 割点、割边的模版

#include<iostream>
using namespace std;
#include<cstdio>
#include<cstring>
#include<vector>
#define N 201
vector<int>G[N];
int n,m,low[N],dfn[N];
bool is_cut[N];
int father[N];
int tim=0;
void input()
{
    scanf("%d%d",&n,&m);
    int a,b;
    for(int i=1;i<=m;++i)
    {
        scanf("%d%d",&a,&b);
        G[a].push_back(b);/*邻接表储存无向边*/
        G[b].push_back(a);
    }
}
void Tarjan(int i,int Father)
{
    father[i]=Father;/*记录每一个点的父亲*/
    dfn[i]=low[i]=tim++;
    for(int j=0;j<G[i].size();++j)
    {
        int k=G[i][j];
        if(dfn[k]==-1)
        {
            Tarjan(k,i);
            low[i]=min(low[i],low[k]);
        }
        else if(Father!=k)/*假如k是i的父亲的话,那么这就是无向边中的重边,有重边那么一定不是桥*/
            low[i]=min(low[i],dfn[k]);//dfn[k]可能!=low[k],所以不能用low[k]代替dfn[k],否则会上翻过头了。
    }
}
void count()
{
    int rootson=0;
    Tarjan(1,0);
    for(int i=2;i<=n;++i)
    {
        int v=father[i];
        if(v==1)
        rootson++;/*统计根节点子树的个数,根节点的子树个数>=2,就是割点*/
        else{
            if(low[i]>=dfn[v])/*割点的条件*/
            is_cut[v]=true;
        }
    }
    if(rootson>1)
    is_cut[1]=true;
    for(int i=1;i<=n;++i)
    if(is_cut[i])
    printf("%d\n",i);
    for(int i=1;i<=n;++i)
    {
        int v=father[i];
        if(v>0&&low[i]>dfn[v])/*桥的条件*/
        printf("%d,%d\n",v,i);
    }
    
}
int main()
{
    input();
    memset(dfn,-1,sizeof(dfn));
    memset(father,0,sizeof(father));
    memset(low,-1,sizeof(low));
    memset(is_cut,false,sizeof(is_cut));
    count();
    return 0;
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值