点双联通分量和边双联通分量如何选择?

先讲一下 ,双联通分量 一定是用于 无向图

考虑什么时候需要用边双联通分量呢?,考虑给你的是一个一般图,需要你把联通的点都缩起来,视作一个点的情况,就是说割点可以反复访问,就是说割点和其他点都是等效的,并没有区别,,(正常情况都是边双联通分量,如果出到点双连通分量,一般难度很高)

点双联通分量使用时,通常,有些点有些特殊,比如,你通过一个环,必须得从一个点入,另一个点出,像下面图,无法通过这个环从1抵达6,这种的情况就需要用点双联通分量缩点

再点双之中,这种可以通过环来从1抵达6

但是在边双中两图完全没有区别

以后可以试试这两种走法是否有区别来判断使用点双还是边双

边双

cpp 复制代码
/*
*/
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define sqrt(a) sqrtl(a)
#define abs(a) llabs(a)
#define pow(a) powl(a)
typedef  pair<int,int> pi ;
#define if1(x) for(int i =1 ;i<=x;i++)
#define if0(x) for(int i = 0;i<x;i++)
#define jf0(x) for(int j = 0;j<x;j++)
#define jf1(x) for(int j = 1;j<=x;j++)
#define pb push_back
const int mod = 1e9+7;
const int inf = 0x3f3f3f3f;
const int N = 2e5+10;



vector<pair<int,int>> edg[N];//终点和边的编号
vector<int> nedg[N];//新边图
int id[N];
int dfn[N],low[N],tim;
stack<int> stk;
vector<vector<int> > dcc;//边双连通分量
bool vis[N];
int cnt,idx;
int n,m;//点数目,边数目
int nidx ;//新点的编号
void tarjan(int u,int in_edg)
{
    dfn[u] = low[u] = ++tim;//更新时间戳
    stk.push(u);vis[u] = 1;
    for(auto &j:edg[u]){
        if(j.second ==(in_edg^1))continue;//是回边,则不走
        if(!dfn[j.first]){//没有访问
            tarjan(j.first,j.second);
            low[u] = min(low[u],low[j.first]);
        }else if(vis[j.first])
            low[u] = min(low[u],low[j.first]);
    }
    if(dfn[u] == low[u]){//桥边到了
        vector<int>te;
        while(1){
            int y = stk.top();stk.pop();
            te.push_back(y);
            vis[y] = 0;
            if(u == y)break;
        }dcc.push_back(te);     
    }
}
void suodian(){
    for(auto j:dcc){
        nidx++;
        for(auto k:j)id[k] = nidx;//标记点的联通 块属于谁
    }
    if1(n){
        for(auto [a,b]:edg[i]){
            if(id[i]!=id[a]){
                nedg[id[i]].push_back(id[a]);
            }
        }
    }
}
void solve(){
    cin>>n>>m;
    idx = 0;
    if0(m){
        int a,b;
        cin>>a>>b;
        edg[a].push_back({b,idx++});
        edg[b].push_back({a,idx++});
    }
    if1(n){
        if(!dfn[i])tarjan(i,-1);
    }
    suodian();
    if1(n)cout<<i<<" "<<id[i]<<"||";
    cout<<endl;
    //输出新图
    if1(nidx){
        cout<<i<<":";
        for(auto j:nedg[i])cout<<j<<" ";
        cout<<endl;
    }

}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr); 
    int t=1;
    // cin>>t;
    while (t--)
    {
        solve();
    }
    return 0;
}
/*
9 11
1 2
2 3
3 1
1 4
1 5
5 6
4 7
1 7
2 7
5 8
9 6
输出
初始边和新边
1 5||2 5||3 5||4 5||5 4||6 2||7 5||8 3||9 1||
新图
1:2
2:4 1
3:4
4:5 2 3
5:4

*/

点双

cpp 复制代码
/*
*/
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define sqrt(a) sqrtl(a)
#define abs(a) llabs(a)
#define pow(a) powl(a)
typedef  pair<int,int> pi ;
#define if1(x) for(int i =1 ;i<=x;i++)
#define if0(x) for(int i = 0;i<x;i++)
#define jf0(x) for(int j = 0;j<x;j++)
#define jf1(x) for(int j = 1;j<=x;j++)
#define pb push_back
const int mod = 1e9+7;
const int inf = 0x3f3f3f3f;
const int N = 2e5+10;




vector<int> edg[N];//终点和边的编号
set<int>  nedg[N];
int dfn[N],low[N],tim;
stack<int> stk;
vector<int > dcc[N];//点双连通分量
bool cut[N];//割点
int id[N];
int cnt,idx,root,nidx;
int n,m;//点数目,边数目
void tarjan(int u)
{
    dfn[u] = low[u] = ++tim;//更新时间戳
    stk.push(u);
    if(!edg[u].size()){//孤立点
        dcc[++cnt].push_back(u);return;
    }
    int child = 0;
    for(auto  j:edg[u]){
        if(!dfn[j]){
            tarjan(j);
            low[u] = min(low[u],low[j]);
            if(low[j] >= dfn[u]){
                child++;
                if(u!=root||child>1){
                    cut[u] = 1;//是割点
                }
                cnt++;
                while(1){
                    int z = stk.top();stk.pop();
                    dcc[cnt].push_back(z);
                    if(z == j)break;
                }
                dcc[cnt].push_back(u);
            }
        }
        else{
            low[u] = min(low[u],dfn[j]);
        }
    }

}
void suodian(){
    nidx = cnt;
    if1(n*m){
        if(cut[i]){
            id[i] = ++nidx;//给割点重新编号

        }
    }
    if1(cnt){
        // cout<<i<<":";
        for(auto j:dcc[i]){
            if(cut[j] == 1){
                nedg[i].insert(id[j]);
                nedg[id[j]].insert(i);
            }
            //割点已经编好新下标了
            else id[j] = i;//以连通块序号来对块中顶点编号
            // cout<<j<<" "<<id[j]<<",";

        }
        // cout<<endl;
    }
}
void solve(){
    cin>>n>>m;
    if0(m){
        int a,b;
        cin>>a>>b;
        edg[a].push_back(b);
        edg[b].push_back(a);
    }
    if1(n){
        if(!dfn[i])root = i,tarjan(i);
    }
    suodian();
    if1(n)cout<<i<<" "<<id[i]<<"||";
    cout<<endl;
    //输出新图
    if1(nidx){
        cout<<i<<":";
        for(auto j:nedg[i])cout<<j<<" ";
        cout<<endl;
    }
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr); 
    int t=1;
    // cin>>t;
    while (t--)
    {
        solve();
    }
    return 0;
}
/*
8 10
1 2
3 4 
2 3
2 4
1 4
1 5
5 6
5 7
5 8
7 8

*/
相关推荐
衡玖2 小时前
c语言闯算法--图(1)
c语言·算法·图论
WW_千谷山4_sch21 小时前
MYOJ_4204:迷宫(图论-网格图基础,dfs,bfs在网格图中应用)
数据结构·c++·深度优先·图论·广度优先
Hfengxiang1 天前
图的代码实现(以邻接矩阵表示)
java·数据结构·算法·图论
查理零世1 天前
【算法】图论 —— SPFA 算法 python
python·算法·图论
yadanuof2 天前
leetcode hot100 图论
leetcode·深度优先·图论
BingLin-Liu2 天前
蓝桥杯备考:图论初解
图论
21号 12 天前
*图论基础(5)
图论
Flower#3 天前
【图论】判断图中有环的两种方法及实现
算法·深度优先·图论
CylMK4 天前
浅说图论基础
算法·深度优先·图论
HIT最菜电控4 天前
代码随想录二刷|图论2
图论