【模板】强连通分量的kosaraju算法实现
来源:互联网 发布:淘宝网渔具店 编辑:程序博客网 时间:2024/06/05 16:09
算法思想
kosaraju算法通过对有向图进行两次Dfs得到强连通分量
第一次dfs对每个节点进行标号,在回溯前给顶点标号,完成标号后,越接近图的尾部(搜索树的叶子),顶点的标号越小。相当于对强连通分量缩点后的DAG进行了一次拓扑排序。
第二次反向dfs,也就是先将图中的边全部反向,在新的图中进行深搜。从标号大的节点(相当于拓扑排序中靠近根节点的节点)开始搜索.
算法正确性的保证:参考:《挑战程序设计竞赛》p321
例题: POJ2186
- 题目大意
N头牛,现在给出M组关系(A,B)表示A喜欢B,喜欢具有传递性,问有多少头牛是被所有牛喜欢。
- 分析
对于每个强连通分量中,牛一定是互相喜欢的,所以我们可以先找出所有的强连通分量再缩点。
缩点后,如果一个节点能被所有节点走到,那么这个节点一定是叶子节点,出度为0(如果出度不为0必然为形成环),而且这样的节点只能有一个(因为这个节点不喜欢其他任何一个节点)
- 代码
#include<cstdio>#include<iostream>#include<cmath>#include<cstring>#include<cstdlib>#include<queue>#include<map>#include<algorithm>#include<set>#include<stack>using namespace std;const int MAXN=10005;const int MAXM=50005;int n,m;bool used[MAXN];int belong[MAXN];//所属强连通分量的拓扑序,belong[i]表示i属于哪个分支int out[MAXN];struct Edge{ int v; int next;}edge[MAXM*2];int edgecount;int head[MAXN];int rhead[MAXN];void Init(){ edgecount=0; memset(head,-1,sizeof(head)); memset(rhead,-1,sizeof(rhead));}void Add_edge1(int u,int v){ edge[++edgecount].v=v; edge[edgecount].next=head[u]; head[u]=edgecount;}void Add_edge2(int u,int v){ edge[++edgecount].v=v; edge[edgecount].next=rhead[u]; rhead[u]=edgecount;}stack<int>Q;void Dfs(int u){ used[u]=1; for(int k=head[u];k!=-1;k=edge[k].next) { int v=edge[k].v; if(!used[v])Dfs(v); } Q.push(u);}void rDfs(int u,int group){ used[u]=1; belong[u]=group; for(int k=rhead[u];k!=-1;k=edge[k].next) { int v=edge[k].v; if(!used[v])rDfs(v,group); }}int scc()//返回强连通分量个数{ int ans=0; memset(used,0,sizeof(used)); while(!Q.empty())Q.pop(); for(int i=1;i<=n;i++) { if(!used[i])Dfs(i); } memset(used,0,sizeof(used)); while(!Q.empty()) { int x=Q.top(); Q.pop(); if(!used[x]){ans++;rDfs(x,ans);} } return ans;}int main(){ int a,b; Init(); while(scanf("%d%d",&n,&m)!=EOF) { memset(out,0,sizeof(out)); for(int i=1;i<=m;i++) { scanf("%d%d",&a,&b); Add_edge1(a,b); Add_edge2(b,a); } int scc_cnt=scc(); for(int u=1;u<=n;u++) { for(int k=head[u];k!=-1;k=edge[k].next) { int v=edge[k].v; if(belong[u]!=belong[v])out[belong[u]]++; } } int sum=0;//统计出度为0的点的块数 int x;//出度为0的连通分量编号 for(int i=1;i<=scc_cnt;i++) { if(out[i]==0){sum++;x=i;} } int ans=0; for(int i=1;i<=n;i++) { if(belong[i]==x)ans++; } if(sum==1)cout<<ans<<endl; else cout<<0<<endl; } return 0;}
阅读全文
0 0
- 【模板】强连通分量的kosaraju算法实现
- 强连通分量Kosaraju算法实现
- 强连通分量的Kosaraju算法
- 强连通分量 的 Kosaraju算法
- 强连通分量 Kosaraju算法
- 强连通分量 -- Kosaraju算法
- 强连通分量-kosaraju算法
- 强连通分量 Kosaraju算法
- 强连通分量(Kosaraju算法)
- 强连通分量Kosaraju、Tarjan【模板】
- poj 2186(强连通分量的kosaraju算法)
- Kosaraju算法 有向图的强连通分量
- Kosaraju算法解析: 求解图的强连通分量
- 强连通分量算法之kosaraju的一些理解
- Kosaraju算法解析: 求解图的强连通分量
- 强连通分量算法Kosaraju 和 Tarjan
- 强连通分量——kosaraju算法
- Kosaraju算法---求解强连通分量
- 文章标题
- POJ-1426--Find The Multiple---BFS广搜
- 组合数学
- POJ 3469 Dual Core CPU(最小割)
- 序列合并
- 【模板】强连通分量的kosaraju算法实现
- cmake使用示例与整理总结
- Linux初始
- 归并排序算法(递归与非递归)
- JQUERY中attr()和css()的区别
- js实现map功能
- 文章标题
- 使用Typescript开发node.js项目——简单的环境配置
- Pick定理