首先引入几个预备知识
1.如果一个无向图中每个顶点从所有其他顶点都是可达的,则称该图是连通的。图的连通分量“从....可达”的等价类.
2.如果一个有向图中任意两点互相可达,则该有向图是强连通的“相互可达”等价类
3.有向图G=(V,E)的强连通分量是一个最大节点集合C包含于V,对于该集合中任意节点u,v,节点u,v可以相互到达
4.深度优先搜索树,每个强连通分量对应一颗深度优先树
5.u,v在图G中可以相互到达当且仅当他们在图G的转置图中可以相互到达
缩点 强连通
注释掉的代码为强连通图判定
既cnt==1;
注意缩点时:只有出度为0的点只有1个时,这个点才能被其他所有点到达
判断强连通以及缩点时不能反向建边因此注释掉
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<stack>
#include<set>
#define ll long long
using namespace std;
const int maxn=1e5+5;
int low[maxn],dfn[maxn],now,root=1;
bool vis[maxn];
int belong[maxn];
int out[maxn];
int cnnt[maxn];
vector<int>g[maxn];
set<int>ans;//缩点
int cnt;//统计强连通分量个数,既深度优先搜索森林中有多少颗树
void init(){
now=0;
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
cnt=0;
memset(vis,0,sizeof(vis));
memset(belong,0,sizeof(belong));
memset(out,0,sizeof(out));
memset(cnnt,0,sizeof(cnnt));
}
void addedge(int u,int v){
g[u].push_back(v);
//g[v].push_back(u);
}
stack<int>st;//存分量图节点
void tarjan(int u){
low[u]=dfn[u]=++now;
vis[u]=1;
st.push(u);
int len=g[u].size();
for(int i=0;i<len;i++){
int v=g[u][i];
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(vis[v]){
low[u]=min(low[u],dfn[v]);
}
}
if(low[u]==dfn[u]){
cnt++;
int top;
while(1){
top=st.top();
st.pop();//分量图节点出栈
belong[top]=cnt;//记录节点属于哪个强连通分量,既属于那颗深度优先搜索树
vis[top]=0;
if(u==top)break;
}
}
}
//if(cnt==1)//整张图是一个强连通。既只有一颗深度优先搜索树
//缩点
//只有出度为零的点的个数等于1时,这个出度为零的点才能被其他所有点到达
int main(){
int n,m;
scanf("%d%d",&n,&m);
init();
while(m--){
int a,b;
scanf("%d%d",&a,&b);
addedge(a,b);
}
for(int i=1;i<=n;i++){
if(!dfn[i]){
tarjan(i);
}
}
for(int i=1;i<=n;i++){
for(int j=0;j<g[i].size();j++){
if(belong[i]!=belong[g[i][j]])//不在一个分量内,且存在出边 既存在横向边
out[belong[i]]++;//出度++
}
cnnt[belong[i]]++;//统计分量内的节点数
}
int temp=0;
int ans;
for(int i=1;i<=cnt;i++){
if(out[i]==0){
temp++;
ans=cnnt[i];
}
}
if(temp==0||temp>1){
cout<<0;
}
else if(temp==1)
cout<<ans;
return 0;
}
割点,割边
求割点,割边时需要反向建边
割点:无向图删掉一点和它所关联的边,图的连通性增加。
割边(桥):无向图删掉一边,图的连通性增加。
求割点
- 根节点有多个子树
- 非根节点u,当儿子节点v:dfn[u] <= low[v]
求割边
割边等价于dfn[u]<low[v]
模板如下
void tarjan(int u,int fa){
low[u]=dfn[u]=++now;//开始时间,完成时间
int len=g[u].size();//获取邻接表的长度
int son=0;//记录树根节点孩子数
for(int i=0;i<len;++i){
int v=g[u][i];
if(v==fa)continue;//后向边
if(!dfn[v]){
tarjan(v,u);
son++;
low[u]=min(low[u],low[v]);
if(u==root&&son>1){
ans.insert(u);
}
else if(u!=root&&dfn[u]<=low[v]){
ans.insert(u);
}
}
else{
low[u]=min(low[u],dfn[v]);
}
}
}
部分内容引自算法导论