Johnson算法论文:https://www.cs.tufts.edu/comp/150GA/homeworks/hw1/Johnson%2075.PDF
Johnson算法讲解视频:https://www.youtube.com/watch?v=johyrWospv0
C++实现代码:
#include <iostream>
#include<vector>
#include<map>
#include<cstring>
using namespace std;
//using johnson algorithm
int visited[10010],dfn[10010],low[10010],ins[10010],stack[10010],color[10010],top=0,cnt,N,M,stamp=0;
vector<int> V[10010],VC[10010];
vector<int> scc[10010];
map<int,vector<int>> blockedmap; //stack,ins 可以重复利用,ins充当blockedset的作用 先用vector,不对就换set
vector<vector<int>> ans; //存放结果
void tarjan(int u){
dfn[u]=low[u]=++stamp;
stack[++top]=u;
ins[u]=1;
for(int i=0;i<V[u].size();i++){
int v=V[u][i];
if(!dfn[v]){tarjan(v);low[u]=min(low[u],low[v]);}
else if(ins[v])
low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u]){
cnt++; int y;
do {
y = stack[top--], ins[y] = 0;
/*c[y] = cnt, */
color[y] = cnt,scc[cnt].push_back(y);
} while (u!= y);
}
}
void stronglycomponent(){
for(int i=1;i<=N;i++){
if(!dfn[i]) tarjan(i);
}
}
void unlock(int u){
ins[u]=0;
if(!blockedmap[u].empty()){
for(auto e:blockedmap[u]){
if(ins[e]) unlock(e);
}
}
blockedmap[u].clear();
}
bool Findcycles(int start,int cur){
//printf("f(%d,%d)\n",start,cur);
bool f=false;// 是否找到环
stack[++top]=cur;
ins[cur]=1;
for(auto e:VC[cur]){
if(!visited[e]){
if(e==start){
//保存结果
vector<int> path;
for(int i=1;i<=top;i++)
path.push_back(stack[i]);
ans.push_back(path);
f=true;
}
else if(!ins[e]){
//bool flag=Findcycles(start,e); //不能合并写
f=(Findcycles(start,e))||f; //次序不能颠倒
}
}
}
if(f) unlock(cur);
else{
for(auto e:VC[cur]){
if(!visited[e]) blockedmap[e].push_back(cur);
}
}
--top;
return f;
}
int main()
{
cin>>N>>M;
//默认编号0-N-1
for(int i=0;i<M;i++){
int a,b;
cin>>a>>b;
V[a].push_back(b);
}
stronglycomponent();
//显示强连通分量
printf("显示强连通分量\n");
cout<<"cnt is "<<cnt<<endl;
for(int i=1;i<=cnt;i++){
for(int j=0;j<scc[i].size();j++){
printf("%d ",scc[i][j]);
}
printf("\n");
}
//缩点
for(int i=1;i<=N;i++){
for(int j=0;j<V[i].size();j++){
if(color[i]==color[V[i][j]]){
VC[i].push_back(V[i][j]);
}
}
}
//寻找所有简单回路
memset(ins,0,sizeof(ins));
for(int startindex=1;startindex<=N;startindex++){
Findcycles(startindex,startindex);
visited[startindex]=1;
}
//显示结果
printf("显示结果\n");
for(int i=0;i<ans.size();i++){
for(int j=0;j<ans[i].size();j++){
printf("%d ",ans[i][j]);
}
printf("\n");
}
}
以视频中的输入例子为测试:
输入:
9 15
1 2
1 5
1 8
2 3
2 7
2 9
3 1
3 2
3 4
3 6
4 5
5 2
6 4
8 9
9 8
输出:
1 2 3
1 5 2 3
2 3
2 3 4 5
2 3 6 4 5
8 9