• 基本思路:
• 这个算法是最通用的算法,其比较关键的部分是同时应用了原图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;
}