什么是强连通分量?在这之前先定义一个强连通性(strong connectivity)的概念:有向图中,如果一个顶点s到t有一条路径,t到s也有一条路径,即s与t互相可达,那么我们说s与t是强连通的。那么在有向图中,由互相强连通的顶点构成的分量,称作强连通分量。
一:对于kosaraju算法,这是一个比tarjan较复杂的算法,但是因为一本通上介绍了这种算法,我就找几个题目练习了一下。
kosaraju算法:可以求出强连通分量的个数,还可以对分属于不同强连通分量的点进行标记。
算法描述:(1):第一次对图G进行DFS遍历,并在遍历的过程中,记录每一个点的退出顺序。
(2):倒转每一条边的方向,构造一个反图G1,然后按照退出的顺序的逆序对反图进行第二次DFS遍历,
(3):每一次遍历得到的那些点属于同一个强连通分量。
因为这就是一个不变的模板,所以直接背过就是最好了
例题:
1298. 通讯问题(来源:sojs.tk)
★ 输入文件:jdltt.in
输出文件:jdltt.out
简单对比
时间限制:1 s 内存限制:128 MB
【题目描述】
一个篮球队有n个篮球队员,每个队员都有联系方式(如电话、电子邮件等)。但并不是每个队员的联系方式都公开,每个队员的联系方式只有一部分队员知道。问队员可以分成多少个小组,小组成员之间可以相互通知(包括一个队员一个组,表示自己通知自己)。
【输入格式】
输入文件有若干行
第一行,一个整数n,表示共有n个队员(2<=n<=100)
下面有若干行,每行2个数a、b,a、b是队员编号,表示a知道b的通讯方式。
【输出格式】
输出文件有若干行
第一行,1个整数m,表示可以分m个小组,下面有m行,每行有若干个整数,表示该小组成员编号,输出顺序按编号由小到大。
【样例输入】
- 12
- 1 3
- 2 1
- 2 4
- 3 2
- 3 4
- 3 5
- 4 6
- 5 4
- 6 4
- 7 4
- 7 8
- 7 12
- 8 7
- 8 9
- 10 9
- 11 10
【样例输出】
8
1 2 3
4 6
5
7 8
9
10
11
12
这道题目的输出顺序是很坑的。
#include<iostream> using namespace std; #include<cstdio> #include<algorithm> #include<vector> #include<cstring> #include<stack> #define N 120 struct Edge{ int u,v,last; }edge1[N],edge2[N]; int t=0; stack<int>s; int ans[N][N]; int pp=0; int head1[N]={0},head2[N]={0}; bool visit[N]={0}; int n; void input() { scanf("%d",&n); int a,b; while(scanf("%d%d",&a,&b)==2) { ++t; edge1[t].u=a; edge1[t].v=b; edge1[t].last=head1[a]; head1[a]=t; edge2[t].u=b; edge2[t].v=a; edge2[t].last=head2[b]; head2[b]=t; } } void dfs1(int k) { visit[k]=true; for(int l=head1[k];l;l=edge1[l].last) { if(!visit[edge1[l].v]) { dfs1(edge1[l].v); } } s.push(k); } void dfs2(int k) { ans[pp][0]++; ans[pp][ans[pp][0]]=k; visit[k]=true; for(int l=head2[k];l;l=edge2[l].last) { if(!visit[edge2[l].v]) { dfs2(edge2[l].v); } } } int kosaraju() { while(!s.empty()) s.pop(); memset(visit,false,sizeof(visit)); for(int i=1;i<=n;++i) { if(!visit[i]) dfs1(i); } memset(visit,false,sizeof(visit)); while(!s.empty()) { int k=s.top(); s.pop(); if(!visit[k]) { pp++; dfs2(k); } } return pp; } int main() { freopen("jdltt.in","r",stdin); freopen("jdltt.out","w",stdout); input(); pp=0; printf("%d ",kosaraju()); for(int i=1;i<=pp;++i) sort(ans[i]+1,ans[i]+ans[i][0]+1); bool flag[N]={0}; for(int i=1;i<=pp;++i) { int q=0; int maxx=(1<<30); for(int j=1;j<=pp;++j) { if(maxx>ans[j][1]&&!flag[j]) { maxx=ans[j][1]; q=j; } } flag[q]=true; for(int j=1;j<=ans[q][0];++j) printf("%d ",ans[q][j]); printf(" "); } fclose(stdin); fclose(stdout); return 0; }
例题二:
619. [金陵中学2007] 传话(来源cojs.tk)
★☆ 输入文件:messagez.in
输出文件:messagez.out
简单对比
时间限制:1 s 内存限制:128 MB
[问题描述]
兴趣小组的同学来自各个学校,为了增加友谊,晚会上又进行了一个传话游戏,如果 a 认识 b ,那么 a 收到某个消息,就会把这个消息传给 b ,以及所有 a 认识的人。
如果 a 认识 b , b 不一定认识 a 。
所有人从 1 到 n 编号,给出所有“认识”关系,问如果 i 发布一条新消息,那么会不会经过若干次传话后,这个消息传回给了 i , 1<=i<=n 。
[输入文件]
输入文件 message.in 中的第一行是两个数 n(n<1000) 和 m(m<10000) ,两数之间有一个空格,表示人数和认识关系数。
接下来的 m 行,每行两个数 a 和 b ,表示 a 认识 b 。 1<=a, b<=n 。认识关系可能会重复给出,但一行的两个数不会相同。
[输出文件]
输出文件 message.out 中一共有 n 行,每行一个字符 T 或 F 。第 i 行如果是 T ,表示 i 发出一条新消息会传回给 i ;如果是 F ,表示 i 发出一条新消息不会传回给 i 。
[输入样例]
4 6
1 2
2 3
4 1
3 1
1 3
2 3
[输出样例]
T
T
T
F
思路:仍然是求强连通分量,只是要判断每一个人是否在一个人数>=2的强连通分量中,如果是就满足题目条件
代码:
#include<iostream> using namespace std; #include<cstdio> int n,m,a,b; struct Edge{ int u,v,last; }; int t=0; #include<stack> #define M 10020 Edge edge1[M],edge2[M]; #define N 1008 stack<int> s; int head1[N],head2[N]; bool visit[N]; #include<algorithm> #include<cstring> bool flag[N]={0}; int ans[N][N]; int calc=0; void input() { scanf("%d%d",&n,&m); for(int i=1;i<=m;++i) { scanf("%d%d",&a,&b); ++t; edge1[t].u=a; edge1[t].v=b; edge1[t].last=head1[a]; head1[a]=t; edge2[t].u=b; edge2[t].v=a; edge2[t].last=head2[b]; head2[b]=t; } } void dfs1(int k) { visit[k]=true; for(int p=head1[k];p;p=edge1[p].last) { if(!visit[edge1[p].v]) dfs1(edge1[p].v); } s.push(k); } void dfs2(int k) { int flat=calc; visit[k]=true; ans[calc][0]++; ans[calc][ans[calc][0]]=k; for(int p=head2[k];p;p=edge2[p].last) if(!visit[edge2[p].v]) { dfs2(edge2[p].v); } if(ans[flat][0]>1)/*判断每一个人是否在一个人数>=2的强连通分量*/ flag[k]=true; } void kosaraju() { while(!s.empty()) s.pop(); memset(visit,false,sizeof(visit)); for(int i=1;i<=n;++i) { if(!visit[i]) dfs1(i); } memset(visit,0,sizeof(visit)); while(!s.empty()) { int k=s.top(); s.pop(); if(!visit[k]) { calc++; dfs2(k); } } } int main() { freopen("messagez.in","r",stdin); freopen("messagez.out","w",stdout); input(); kosaraju(); for(int i=1;i<=n;++i) if(flag[i]) printf("T "); else printf("F "); fclose(stdin); fclose(stdout); return 0; }