笔记
1、出度:以顶点v为起点的弧的数目
入度:顶点v为终点的弧的数目
2、如果在有向图G中,有一条<u,v>有向道路,则v称为u可达的,或者说,从u可达v。
3、强连通图:若有向图G的任意两个顶点都互相可达,则称图 G是强连通图,如果有向图G存在两顶点u和v使得u不能到v,或者v不能到u,则称图G是强非连通图。
4、强连通分量:如果有向图G不是强连通图,他的子图G2是强连通图,点v属于G2,任意包含v的强连通子图也是G2的子图,则称G2是有向图G的极大强连通子图,也称强连通分量。
5、极大强连通子图(强连通分量)解释:一个图的强连通子图,并且加入任何一个不在它的点集中的点都会导致它不再强连通。
6、强连通分量里边的任意两个点,都是互相可达的。
算法解析
1、Tarjan算法,是一个基于Dfs(深搜)的算法,假设我们要先从0号节点开始Dfs,我们发现一次Dfs我门能遍历整个图,而且我们发现,在Dfs的过程中,我们深搜到 了其他强连通分量中,那么我们Dfs之后如何判断哪个和那些节点属于一个强连通分量呢?
之所以用DFS就是因为它将每一个强连通分量作为搜索树上的一个子树。而这个图,就是一个完整的搜索树。
为了使这颗搜索树在遇到强连通分量的节点的时候能顺利进行。每个点都有两个参数。
2、首先引入两个数组
int dfn[]; //点搜索的次序编号 int low[]; //每个点在这颗树中的,最小的子树的根
3、Tarjan算法在搜索过程中把没有Tarjan过的点入栈并将该节点的dfn[i] = low[i] = ++dfs_num(也就是设成他自己),然后以这个节点为树根再进行搜索,当一颗子树搜索完毕时回溯,并在回溯时比较当前节点和目标节点的LOW值,将较小的LOW值赋给当前结点的LOW,这样可以保证每个节点在以其为树根的子树的所有节点中LOW值是最小的。如果回溯时发现当前节点DFN[i]==LOW[i],就将栈中当前结点以上的节点全部弹栈,这些点就组成了一个强连通分量。还要注意一点是,当目标节点进行过Tarjan但还在栈中,就拿当前节点LOW值与目标节点DFN值比较,把更小的赋给当前结点的LOW。
①对于从没访问过的节点:加入栈中让其DFN[i]=LOW[i]=++dfs_num,让vis[i]=1表示该点入栈了。这类点的标志是!DFN[i]&&!vis[i]。
②对于访问过但不在栈中的节点(!vis[i])直接回溯即可,因为既然该节点访问过了又不在栈中,就必定属于另一个强连通分量。这类点的标志是DFN[i]&&!vis[i]。
②对于访问过且在栈中的节点,比较当前节点LOW值和目标节点DFN值,将较小的赋给当前结点LOW值然后回溯。这类点的标志是DFN[i]&&vis[i]。
算法模板
评测:洛谷:P2863
#include <cstdio> #include <cstring> #include <cmath> #include <cstdlib> #include <iostream> #include <algorithm> #include <vector> #include <stack> using namespace std; const int MAXN = 100005; const int MAXM = 500005; struct node { int next,to; } e[MAXN]; int cnt,num,n,m,dfn[MAXN],low[MAXN],head[MAXN],ans; int cut[MAXN]; int all; bool vis[MAXN]; stack< int > s; void add(int ,int ); void work(); void Tarjan(int ); int main() { cin >> n >> m; for(int i = 1;i <= m;++i) { int from,to; cin >> from >> to; add(from,to); } work(); return 0; } void add(int from,int to) { e[++cnt].next = head[from]; e[cnt].to = to; head[from] = cnt; } void work() { for(int i = 1;i <= n;++i) if(!dfn[i]) Tarjan(i); for(int i = 1;i <= all;++i) if(cut[i] > 1) ans++; cout << ans << endl; } void Tarjan(int u) { dfn[u] = low[u] = ++num; s.push(u); vis[u] = true; for(int i = head[u];i;i = e[i].next) { int v = e[i].to; if(!dfn[v]) { Tarjan(v); low[u] = min(low[u],low[v]); } else if(vis[v]) low[u] = min(low[u],dfn[v]); } if(dfn[u] == low[u]) { all++; while(s.top() != u) { int k = s.top(); s.pop(); vis[k] = false; cut[all]++; } s.pop(); cut[all]++; vis[u] = false; } }
参考:
https://blog.csdn.net/mengxiang000000/article/details/51672725
https://blog.csdn.net/qq_34374664/article/details/77488976
https://www.cnblogs.com/reddest/p/5932153.html
文章评论