拓扑排序
还有给定一个 DAG,如果从 u 到 v 有边,则认为 v 依赖于 u 。如果 u 到 v 有路径( u 可达 v ),则称 v 间接依赖于 u 。
拓扑排序的目标是将所有节点排序,使得排在前面的节点不能依赖于排在后面的节点。
Kahn算法
bool toposort(){
q = new queue();
for(int i =0;i < n;i++)
if(in_deg[i] == 0 ) q.push(i);
ans = new vector();
while(! q.empty())
{
u = q.pop();
ans.push_back();
for each edge(u,v){
if(--in_deg[v] == 0) q.push(v);
}
}
if(ans.size() == n){
for(int i = 0;i < n;i++)
cout << ans[i] << endl;
return true;
} else {
return false;
}
}
强连通分量
第一遍 dfs 确定原图的逆后序序列,即 4 7 6 5 1 2 3 8
• 第二遍 dfs 在反图中按照逆后序序列进行遍历
• 反图即将原图中的有向边反向
• 每次由起点遍历到的点即构成一个 SCC
Kosaraju 代码
void dfs1(int x){
vis[x] = 1;
for(auto y:G1[x])
if(!vis[y]) difs1(y);
dfn[++dcnt] = x;//确定后序
}
void dfs2(int x){
c[x] = scnt;//赋值来连通分量的标号
for(auto y:G2[x])
if(!c[y]) dfs2(y);
}void kosaraju(){
dcnt = scnt =0;
memset(c,0,sizeof(c));
memset(vis,0,sizeof(vis));
for(int i=1;i <= n;i++)
if(!vis[i]) dfs1(i);
for(int i = n;i >= 1;i--)//按照逆后序的顺序遍历
if(!c[dfn[i]])
{
++scnt;
dfs2(dfn[i]);
}
}
将强连通分量缩点
void shrinkPoints()
{
for(int i = 1;i<=n;i++)
for(auto y:G1[i])//遍历图中的每一条边
{
if(c[i] == c[y])
continue;//属于同一个连通分量
int u = c[i];
int v = c[y];
G3[u].push_back(v);
}
}