强连通分量
笔记本的独显坏了,在图书馆打的,真是痛苦。写个最基本的算法写了2个小时。神船真是半年包烂,不玩游戏还是别上船了。
有向图强连通分量:在有向图G中,如果两个顶点vi,vj间(vi>vj)有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。有向图的极大强连通子图,称为强连通分量(strongly connected components)。
基本的实现是Kosaraju算法:利用两次DFS,第一次对正向图进行DFS,得出一个森林,再对反向图进行一次DFS。注意强连通分量只能存在单棵树中,所以我们如果普通的搜索可能从一棵树中搜索到另一棵树,所以我们采用倒序,因为最后一棵树一定满足对于当前图不能搜索到其他树。下面是代码实现:
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn=1050;
int num[maxn];
bool flag[maxn];
vector<int> map[maxn],Rmap[maxn],s;
int n,m,cnt;
void dfs1(int u)
{
flag[u]=true;
for(int i=0;i<map[u].size();i++)
{
if(!flag[map[u][i]])
dfs1(map[u][i]);
}
s.push_back(u);//将每棵树的顶点存储
}
void dfs2(int u)
{
flag[u]=true;
num[u]=cnt;
for(int i=0;i<Rmap[u].size();i++)
{
if(!flag[Rmap[u][i]])
dfs2(Rmap[u][i]);
}
}
int main()
{
int u,v;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&u,&v);
map[u].push_back(v);
Rmap[v].push_back(u);
}
memset(flag,false,sizeof(flag));
for(int i=1;i<=n;i++)
{
if(!flag[i])
dfs1(i);
}
memset(flag,false,sizeof(flag));
cnt=0;
for(int i=s.size()-1;i>=0;i--)//对反向图进行DFS,注意从后到前,避免搜索到其他树
{
if(!flag[s[i]])
{
++cnt;
dfs2(s[i]);
}
}
printf("强连通分量个数:\n");
printf("%d\n",cnt);
vector<int> ans[maxn];
for(int i=1;i<=n;i++)
{
ans[num[i]].push_back(i);
}
printf("对应的强连通分量分别为:\n");
for(int i=1;i<=cnt;i++)
{
for(int j=0;j<ans[i].size();j++)
printf("%d ",ans[i][j]);
printf("\n");
}
return 0;
}