有向图中在若两点之间可以互相到达,则称这两点强连通,如果一个点集内的所有点都可以互相到达,那么这个点集就是图的一个强连通分量,而我们需要找出有向图中的所有极大强连通分量,于是就用Tarjan算法进行强连通,并将一个连通块缩成一个点,这样就可以形成了一张有向无环图,对解题会很有帮助。
找强连通分量的方法就是 dfs 寻找某个点以及它的后继节点能够到达的最远祖先节点,如果这个最远祖先节点就是进入 dfs 的点,说明所有搜到的后继节点都是在这个强连通分量中,就依次将他们标记为同一个强连通分量。
head、point、nxt、size都是实现链式前向星的,开了两个,下标 0 是原图,下标 1 是缩点后的图。n 为总点数,t 是 dfs 的时间轴,用来记录dfs树中的先后访问顺序以及判断关于后继点到的最远祖先节点的标号。stx就是每个节点的在 dfs 时间轴上的标号,low 是每个节点能够通过其后继节点到达的最远的祖先节点,scc 是每个节点所属的强连通分量的编号,scccnt 是强连通分量的总个数。
主体是从大白书的模板上修改来的。
有注释:
1 #include<stdio.h> //需要这些头文件
2 #include<string.h>
3 #include<stack>
4 #include<queue>
5 using namespace std;
6
7 //maxn为点总数,maxm为边总数(原图)
8 const int maxn=1005;
9 const int maxm=6005;
10
11 int head[2][maxn],point[2][maxm],nxt[2][maxm],size[2]; //0是原图,1是缩点后的图
12 int n,t,scccnt; //n为点数,t是时间轴,scccnt是强连通分量数
13 int stx[maxn],low[maxn],scc[maxn]; //stx是点的时间轴标号,low是能达到的最远祖先节点,scc是点所属强连通分量编号
14 stack<int>S; //用来存强连通分量内的点,并最后依次加入强连通分量
15
16 void init(){ //原图的初始化
17 memset(head,-1,sizeof(head));
18 size[0]=size[1]=0;
19 }
20
21 void add(int a,int b,int c=0){ //加边函数,原图是0,建原图时可以不传入参数c,缩点图是1
22 point[c][size[c]]=b;
23 nxt[c][size[c]]=head[c][a];
24 head[c][a]=size[c]++;
25 }
26
27 void dfs(int s){
28 stx[s]=low[s]=++t; //节点的时间轴标号,其能访问的起始最远祖先是它自己
29 S.push(s); //将s点压入栈
30 for(int i=head[0][s];~i;i=nxt[0][i]){
31 int j=point[0][i];
32 if(!stx[j]){ //如果j还没有被访问过,就说明不存在与之前已经定的强连通分量中,就从此点继续搜索
33 dfs(j);
34 low[s]=min(low[s],low[j]); //用后继点更新自己能到达的最远祖先
35 }
36 else if(!scc[j]){ //如果scc[j]已经有值,说明j已经是其他强连通分量中的点,那么对它 dfs 时没有搜索到s点说明s点和j点不在同一强连通分量中,所以不需要处理
37 low[s]=min(low[s],stx[j]);
38 }
39 }
40 if(low[s]==stx[s]){ //自己及后继能访问的最远祖先就是自己,那么从这之后的所有点都是这个强连通分量的点,从栈中取出点依次标记
41 scccnt++;
42 while(1){
43 int u=S.top();S.pop();
44 scc[u]=scccnt;
45 if(s==u)break;
46 }
47 }
48 }
49
50 void setscc(){
51 memset(stx,0,sizeof(stx));
52 memset(scc,0,sizeof(scc));
53 t=scccnt=0;
54 for(int i=1;i<=n;++i)if(!stx[i])dfs(i); //依次dfs
55 for(int i=1;i<=n;++i){
56 for(int j=head[0][i];~j;j=nxt[0][j]){
57 int k=point[0][j];
58 if(scc[i]!=scc[k]){ //两点不在同一个强连通分量中,那么这两个强连通分量就继承两点的有向边。
59 add(scc[i],scc[k],1);
60 }
61 }
62 }
63 }
木有注释:
1 #include<stdio.h>
2 #include<string.h>
3 #include<stack>
4 #include<queue>
5 using namespace std;
6
7 const int maxn=1005;
8 const int maxm=6005;
9
10 int head[2][maxn],point[2][maxm],nxt[2][maxm],size[2];
11 int n,t,scccnt;
12 int stx[maxn],low[maxn],scc[maxn];
13 stack<int>S;
14
15 void init(){
16 memset(head,-1,sizeof(head));
17 size[0]=size[1]=0;
18 }
19
20 void add(int a,int b,int c=0){
21 point[c][size[c]]=b;
22 nxt[c][size[c]]=head[c][a];
23 head[c][a]=size[c]++;
24 }
25
26 void dfs(int s){
27 stx[s]=low[s]=++t;
28 S.push(s);
29 for(int i=head[0][s];~i;i=nxt[0][i]){
30 int j=point[0][i];
31 if(!stx[j]){
32 dfs(j);
33 low[s]=min(low[s],low[j]);
34 }
35 else if(!scc[j]){
36 low[s]=min(low[s],stx[j]);
37 }
38 }
39 if(low[s]==stx[s]){
40 scccnt++;
41 while(1){
42 int u=S.top();S.pop();
43 scc[u]=scccnt;
44 if(s==u)break;
45 }
46 }
47 }
48
49 void setscc(){
50 memset(stx,0,sizeof(stx));
51 memset(scc,0,sizeof(scc));
52 t=scccnt=0;
53 for(int i=1;i<=n;++i)if(!stx[i])dfs(i);
54 for(int i=1;i<=n;++i){
55 for(int j=head[0][i];~j;j=nxt[0][j]){
56 int k=point[0][j];
57 if(scc[i]!=scc[k]){
58 add(scc[i],scc[k],1);
59 }
60 }
61 }
62 }
原文:http://www.cnblogs.com/cenariusxz/p/4793338.html