图的连通性

Posted LINNO

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了图的连通性相关的知识,希望对你有一定的参考价值。

title : 图的连通性
date : 2021-8-10
tags : ACM,图论,连通性

 

强连通分量(SCC)

有向图强连通分量:在有向图G中,如果两个顶点vi,vj间(vi>vj)有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。有向图的极大强连通子图,称为强连通分量。

缩点

将有向有环图中的环缩成一个个点,形成一个有向无环图。

第一步是要找环,使用tarjan算法dfs遍历整个图,用栈来保存当前所在路径上的所有点,一旦无法往下走就退栈并增加连通分量。

//luoguP3387 【模板】缩点
#include<bits/stdc++.h>
using namespace std;

const int maxn=10005;
int n,m,u,v,a[maxn];
int tot,head[maxn];//tot,head用于链式前向星
int idx,bel[maxn],dfn[maxn],low[maxn];
//idx为当前时间戳,bel为连通分量,dfn为点的访问时间,low(u)为分量最早的访问时间
int stk[maxn],top,vis[maxn]; //stk为手写栈,top为栈顶,vis标记访问
int ru[maxn],dist[maxn];
//ru为入度,dist[i]为以i为终点的最大点权

struct E{
int to,next,from;
}e[maxn*10];

vector<int>G[maxn]; //存放缩点后的图,便于区分

void addedge(int x,int y){ //链式前向星建初始图
e[++tot].next=head[x];
e[tot].from=x;
e[tot].to=y;
head[x]=tot;
}

void tarjan(int x){
low[x]=dfn[x]=++idx; //时间戳和最早时间
stk[++top]=x; //入栈
vis[x]=1;//标记已经访问
for(int i=head[x];i;i=e[i].next){
int v=e[i].to;
if(!dfn[v]){ //如果没访问过下一个节点
tarjan(v); //访问下一个节点
low[x]=min(low[x],low[v]); //如果形成环,承接下一个节点的最早时间
}else if(vis[v]){ //如果已经访问过下一个节点
low[x]=min(low[x],low[v]); //这貌似是有争议的地方
}
}
if(dfn[x]==low[x]){ //x是一个连通分量
int y;
while(y=stk[top--]){ //访问的元素出栈
bel[y]=x; //y属于连通分量x
vis[y]=0; //这句话少了就50分,想一想蝴蝶结的情况就知道啦
if(x==y) break;
a[x]+=a[y]; //在本题中缩点后把点权相加
}
}
}

int topo(){ //拓扑排序
queue<int>q;
for(int i=1;i<=n;i++){
if(bel[i]==i&&!ru[i]){ //如果该点是连通分量并且入度为0
q.push(i);
dist[i]=a[i]; //记录该点为终点的点权之和
}
}
while(!q.empty()){
int fro=q.front();q.pop();
for(int i=0;i<G[fro].size();i++){
int to=G[fro][i];
dist[to]=max(dist[to],dist[fro]+a[to]);
ru[to]--;
if(!ru[to]) q.push(to); //入度为0的点入队
}
}
int ans=0;
for(int i=1;i<=n;i++) ans=max(ans,dist[i]); //记录答案
return ans;
}

signed main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=m;i++){
cin>>u>>v;
addedge(u,v); //建单向边
}
for(int i=1;i<=n;i++){
if(!dfn[i]) tarjan(i); //tarjan缩点
}
for(int i=1;i<=m;i++){ //对于每条边
int x=bel[e[i].from],y=bel[e[i].to];
if(x!=y){ //如果处于不同的连通分量,则建边
G[x].push_back(y);
ru[y]++;
}
}
printf("%d",topo());
return 0;
}
割点

无向连通图中,某点和连接点的边去掉后,图不再连通。

在tarjan中,对于某一个顶点u,如果存在至少一个顶点v(u的儿子),使得low[v]>=dfn[u],即不能回到祖先,那么u点为割点。

判断割点的两个条件:

①x是根节点并且度数>1

②x不是根节点并且dfn[x]<=low[y],y为x的儿子

void tarjan(int x){ //求割点
   dfn[x]=low[x]=++num;
   int col=0;
   for(int i=head[x];i;i=e[i].next){
       int y=e[i].to;
       if(!dfn[y]){
           col++;
           tarjan(y);
           low[x]=min(low[x],low[y]);
           if((x==root&&col>1)||(x!=root&&dfn[x]<=low[y]))
               is[x]=1;//这个点是割点
      }else low[x]=min(low[x],dfn[y]);
  }
}
桥(割边)

无向连通图中,去掉一条边,图中的连通分量数增加,则这条边,称为桥或者割边。

void tarjan(int x){ //求桥
   dfn[x]=low[x]=++idx;
   for(int i=head[x];i;i=e[i].next){
       if(i==f[x]^1) continue;//反向边跳过
       int v=e[i].to;
if(!dfn[v]){
           f[v]=i;
           tarjan(v);
           low[x]=min(low[x],low[v]);
           if(low[v]>dfn[x]) ans++,cut[i]=cut[i^1]=1;//桥
      }else low[x]=min(low[x],dfn[v]);
  }
}

 

 

双连通分量(BCC)

边双连通

若一个无向图中的去掉任意一条边都不会改变此图的连通性,即不存在桥,则称作边双连通图。

点双连通

若一个无向图中的去掉任意一个节点都不会改变此图的连通性,即不存在割点,则称作点双连通图。

 

参考资料

https://blog.csdn.net/acmmmm/article/details/16361033

https://www.luogu.com.cn/blog/juruohyfhaha/post-ti-xie-su-dian

https://www.cnblogs.com/ljy-endl/p/11595161.html

以上是关于图的连通性的主要内容,如果未能解决你的问题,请参考以下文章

图的连通性

图的连通性:强连通-Tarjan算法

图的连通性算法

10.4图的连通性(Connectivity)

图的连通性算法

通过删除单个节点来降低图的连通性的最佳算法是啥? [关闭]