强连通分量Kosaraju算法

 基本思路:
  这个算法是最通用的算法,其比较关键的部分是同时应用了原图G和反图GT(步骤1)先用对原图G进行深搜记录时间结f(n)。(步骤2)选择f(n)最大的点在深搜得到各个强连通分量。
2. 伪代码
  Kosaraju_Algorithm:
  step1:对原图G进行深度优先遍历,记录每个节点的离开时间。
  step2:选择具有最晚离开时间的顶点,对反图GT进行遍历,删除能够遍历到的顶点,这些顶点构成一个强连通分量。
  step3:如果还有顶点没有删除,继续step2,否则算法结束。
如下图所视:
 
 

poj2186
#include <iostream>
#include
<cstring>
#include
<vector>
using namespace std;
const int MAX = 105;/****** * v为原图 * rv为所有边逆向后的新图 * s记录第一遍dfs后各点的结束时间:最晚结束的在栈顶 ******/
vector
<int> v[MAX], rv[MAX], s;/****** * num[v]点v所属连通分量的编号 * tree_n[i]第i个连通分量内点的个数 ******/
int num[MAX], tree_n[MAX], cnt;
bool mark[MAX],ml[MAX];
void dfs_1(int x)//第一步计算dfs结束时间,将时间由小到大对应的数入s
{
mark[x]
= 1;
for (int i = 0; i < v[x].size(); ++i)
{
if (!mark[v[x][i]])
{
dfs_1(v[x][i]);
}
}
s.push_back(x);
}

void dfs_2(int x)//从s的尾取数再rv中dfs
{
num[x]
= cnt; //点v所属连通分量的编号为cnt
++tree_n[cnt]; //记录编号为cnt的连通分量中的元数个数
mark[x] = 1;
for (int i = 0; i < rv[x].size(); ++i)
{
if (!mark[rv[x][i]])
{
dfs_2(rv[x][i]);
}
}
}
void cal(int n)
{
int i,j;
memset(mark,
0, sizeof(mark));
memset(ml,
0,sizeof(ml));
for (i = 1; i <= n; ++i)
{
for (j = 0; j < v[i].size(); ++j)
{
int x = v[i][j];
if (num[i] != num[x])//检查强连通分量是否有出度,有就对应的mark[i]=1
{
mark[num[x]]
= 1;
ml[num[i]]
=1;
}
}
}
int flag = 0,flg =0, ans;
for (i = 1; i <= cnt; ++i) //检查有几个强连通分量的入度为0
{
if (!mark[i])
{
ans
= i;
++flag;
}
if (!ml[i])
{
++flg;
}
}
cout
<<flag<<endl;
if(cnt!=1)
{
flg
=flag>flg? flag:flg;
cout
<<flg<<endl;
}
else cout<<0<<endl;
}

int main()
{

int a, b, n, i, j;
scanf(
"%d",&n);
for(i=1;i<=n;i++)
{
while(cin>>a&&a)
{
v[i].push_back(a);
rv[a].push_back(i);
}

}

for (i = 1; i <= n; ++i)
{
if (!mark[i])
{
dfs_1(i);
}
}
memset(mark,
0, sizeof(mark));
memset(tree_n,
0, sizeof(tree_n));
cnt
= 0; //cnt记录连通分量的个数
for (i = s.size() - 1; i >= 0; --i)
{
if (!mark[s[i]])
{
++cnt;
dfs_2(s[i]);
}
}
cal(n);
return 0;
}
poj1236
#include <iostream>
#include
<cstring>
#include
<vector>
using namespace std;
const int MAX = 10005;/****** * v为原图 * rv为所有边逆向后的新图 * s记录第一遍dfs后各点的结束时间:最晚结束的在栈顶 ******/
vector
<int> v[MAX], rv[MAX], s;/****** * num[v]点v所属连通分量的编号 * tree_n[i]第i个连通分量内点的个数 ******/
int num[MAX], tree_n[MAX], cnt;bool mark[MAX];
void dfs_1(int x)//第一步计算dfs结束时间,将时间由小到大对应的数入s
{
mark[x]
= 1;
for (int i = 0; i < v[x].size(); ++i)
{
if (!mark[v[x][i]])
{
dfs_1(v[x][i]);
}
}
s.push_back(x);
}

void dfs_2(int x)//从s的尾取数再rv中dfs
{
num[x]
= cnt; //点v所属连通分量的编号为cnt
++tree_n[cnt]; //记录编号为cnt的连通分量中的元数个数
mark[x] = 1;
for (int i = 0; i < rv[x].size(); ++i)
{
if (!mark[rv[x][i]])
{
dfs_2(rv[x][i]);
}
}
}
void cal(int n)
{
int i,j;
memset(mark,
0, sizeof(mark));
for (i = 1; i <= n; ++i)
{
for (j = 0; j < v[i].size(); ++j)
{
int x = v[i][j];
if (num[i] != num[x])//检查强连通分量是否有出度,有就对应的mark[i]=1
{
mark[num[i]]
= 1;
}
}
}
int flag = 0, ans;
for (i = 1; i <= cnt; ++i) //检查有几个强连通分量的入度为0
{
if (!mark[i])
{
ans
= i;
++flag;
}
}
if (flag == 1) //如果只有一个强连通分量的入度为0,则输出应的tree_n[i]为要的答案,否则没有
{
printf(
"%d\n", tree_n[ans]);
}
else
{
printf(
"0\n");
}
}

int main()
{

int a, b, n, m, i, j;
scanf(
"%d%d", &n, &m);

for (i = 0; i < m; ++i)
{
scanf(
"%d%d", &a, &b);
v[a].push_back(b);
rv[b].push_back(a);
}

for (i = 1; i <= n; ++i)
{
if (!mark[i])
{
dfs_1(i);
}
}
memset(mark,
0, sizeof(mark));
memset(tree_n,
0, sizeof(tree_n));
cnt
= 0; //cnt记录连通分量的个数
for (i = s.size() - 1; i >= 0; --i)
{
if (!mark[s[i]])
{
++cnt;
dfs_2(s[i]);
}
}
cal(n);
return 0;
}
posted @ 2011-08-04 20:21  qijinbiao1  阅读(3494)  评论(2编辑  收藏  举报