有向图的强联通分量
强联通分量:是指点的一个子集以及这些点附带的边,这个子集中任意两点能互相到达对方。
算法描述:
1. 用dfs遍历有向图,形成一个森林(注意是森林),遍历时记下每个节点 i 所有子孙访问完成的时刻。如 i 有子孙a,b,c,则dfs(a), dfs(b), dfs(c)后将时间戳赋给 finish[i]。
2. 将所有边反向,构成新的图GT
3. 在GT上按点的时间戳降序做dfs,生成一个森林,每棵树就是一个强联通分量了。
强联通分量的性质:
1. 若 u,v 处于同一强联通分量中,则所有边反向后,它们任处在同一强联通分量中。
2. 在第二次 dfs 中,设点 u 的时间戳是当前所有未访问的点中最大的,则 u 所在的强联通分量 C 没有边指向其它未访问的强联通分量C'(注意是在G T中)。因为若存在这样的边,则在G中存在边从 C' 的点指向 C,则C‘中点时间戳肯定比 u 大(回想一下第一次 dfs 时我们是怎样对点标时间戳的),与假设矛盾。
下面是代码,最后是测试文件。输出每个点属于哪个强联通分量。
#include
<
iostream
>
#include < list >
#include < algorithm >
using namespace std;
const int maxn = 100 ;
// 用于第一次dfs时记录每个点的finish时间戳
struct finish{
int id;
int ftime;
}f[maxn];
// 这个结构体用于第二次dfs时记录每个点属于哪个块
struct component{
int cid;
int id;
}comp[maxn];
// 排序用
bool operator < ( const finish & a, const finish & b)
{
return a.ftime < b.ftime;
}
// 邻接表存储
list < int > l[maxn];
list < int > lr[maxn];
// dfs访问标识数组
int visit[maxn];
int t;
int n,e;
void dfs1( int k, int id)
{
visit[k] = id;
for (list < int > ::iterator it = l[k].begin();it != l[k].end();it ++ ){
int x = ( int )( * it);
if (visit[x] ==- 1 ) dfs1(x,id);
}
f[k].ftime =++ t;
f[k].id = k;
}
void dfs2( int k, int cid)
{
visit[k] = cid;
comp[k].id = k;
comp[k].cid = cid;
for (list < int > ::iterator it = lr[k].begin();it != lr[k].end();it ++ ){
int x = ( int )( * it);
if (visit[x] ==- 1 ) dfs2(x,cid);
}
}
// 将图的边反向
void reverse()
{
for ( int i = 0 ;i < n;i ++ ){
for (list < int > ::iterator it = l[i].begin();it != l[i].end();it ++ ){
lr[( int )( * it)].push_back(i);
}
}
}
void clearList()
{
int i;
for (i = 0 ;i < n;i ++ ) l[i].clear(),lr[i].clear();
}
bool read()
{
int i,j,k;
if (scanf( " %d%d " , & n, & e) == EOF) return false ;
for (i = 0 ;i < e;i ++ ){
scanf( " %d%d " , & j, & k);
l[j - 1 ].push_back(k - 1 );
}
return true ;
}
void solve()
{
int i,j,k;
memset(visit, - 1 ,sizeof(visit));
// 第一次dfs记录时间戳
int id = 1 ;
t = 0 ;
for (i = 0 ;i < n;i ++ ) if (visit[i] ==- 1 ){
dfs1(i,id ++ );
}
// for(i=0;i<n;i++) printf("%d ",visit[i]);
// printf("\n");
// 根据finish的时间戳来排序
sort(f,f + n);
// 所有边反向
reverse();
for (i = n - 1 ;i >= 0 ;i -- ) printf( " %d " ,f[i].id);
printf( " \n " );
memset(visit, - 1 ,sizeof(visit));
int cid = 1 ;
for (i = n - 1 ;i >= 0 ;i -- ){
int x = f[i].id;
if (visit[x] ==- 1 ) dfs2(x,cid ++ );
}
for (i = 0 ;i < n;i ++ ) printf( " %d " ,comp[i].cid);
}
int main()
{
freopen( " graph.txt " , " r " ,stdin);
while (read()){
solve();
clearList();
}
}
// 测试文件,包括两个图,第一个是联通的,第二个不联通
8 14
1 3
2 1
3 2
3 4
2 4
3 6
4 5
5 4
6 5
6 7
7 6
5 8
7 8
8 8
4 4
1 2
2 1
3 4
4 3
#include < list >
#include < algorithm >
using namespace std;
const int maxn = 100 ;
// 用于第一次dfs时记录每个点的finish时间戳
struct finish{
int id;
int ftime;
}f[maxn];
// 这个结构体用于第二次dfs时记录每个点属于哪个块
struct component{
int cid;
int id;
}comp[maxn];
// 排序用
bool operator < ( const finish & a, const finish & b)
{
return a.ftime < b.ftime;
}
// 邻接表存储
list < int > l[maxn];
list < int > lr[maxn];
// dfs访问标识数组
int visit[maxn];
int t;
int n,e;
void dfs1( int k, int id)
{
visit[k] = id;
for (list < int > ::iterator it = l[k].begin();it != l[k].end();it ++ ){
int x = ( int )( * it);
if (visit[x] ==- 1 ) dfs1(x,id);
}
f[k].ftime =++ t;
f[k].id = k;
}
void dfs2( int k, int cid)
{
visit[k] = cid;
comp[k].id = k;
comp[k].cid = cid;
for (list < int > ::iterator it = lr[k].begin();it != lr[k].end();it ++ ){
int x = ( int )( * it);
if (visit[x] ==- 1 ) dfs2(x,cid);
}
}
// 将图的边反向
void reverse()
{
for ( int i = 0 ;i < n;i ++ ){
for (list < int > ::iterator it = l[i].begin();it != l[i].end();it ++ ){
lr[( int )( * it)].push_back(i);
}
}
}
void clearList()
{
int i;
for (i = 0 ;i < n;i ++ ) l[i].clear(),lr[i].clear();
}
bool read()
{
int i,j,k;
if (scanf( " %d%d " , & n, & e) == EOF) return false ;
for (i = 0 ;i < e;i ++ ){
scanf( " %d%d " , & j, & k);
l[j - 1 ].push_back(k - 1 );
}
return true ;
}
void solve()
{
int i,j,k;
memset(visit, - 1 ,sizeof(visit));
// 第一次dfs记录时间戳
int id = 1 ;
t = 0 ;
for (i = 0 ;i < n;i ++ ) if (visit[i] ==- 1 ){
dfs1(i,id ++ );
}
// for(i=0;i<n;i++) printf("%d ",visit[i]);
// printf("\n");
// 根据finish的时间戳来排序
sort(f,f + n);
// 所有边反向
reverse();
for (i = n - 1 ;i >= 0 ;i -- ) printf( " %d " ,f[i].id);
printf( " \n " );
memset(visit, - 1 ,sizeof(visit));
int cid = 1 ;
for (i = n - 1 ;i >= 0 ;i -- ){
int x = f[i].id;
if (visit[x] ==- 1 ) dfs2(x,cid ++ );
}
for (i = 0 ;i < n;i ++ ) printf( " %d " ,comp[i].cid);
}
int main()
{
freopen( " graph.txt " , " r " ,stdin);
while (read()){
solve();
clearList();
}
}
// 测试文件,包括两个图,第一个是联通的,第二个不联通
8 14
1 3
2 1
3 2
3 4
2 4
3 6
4 5
5 4
6 5
6 7
7 6
5 8
7 8
8 8
4 4
1 2
2 1
3 4
4 3