强连通分量之Korasju 与tarjan两小模版题
2016-01-16 21:21
399 查看
Korasaju算法求有向图强连通分支
procedure Strongly_Connected_Components(G);
begin
1.深度优先遍历G,算出每个结点u的结束时间f[u],起点如何选择无所谓。
2.深度优先遍历G的转置图GT, 选择遍历的起点时,按照结点的结束时间从大到小进行。遍历的过程中,一边遍历, 一边给结点做分类标记,每找到一个新的起点,分类标记值就加1。
3. 第2步中产生的标记值相同的结点构成深度优先森林中的一棵树,也即一个强连通分量。
end.
poj2186.
题意比较简单就不写了。
解题思路:
1. 求出所有强连通分量
2. 每个强连通分量缩成一点,则形成一个有向无环图DAG。
3. DAG上面如果有唯一的出度为0的点,则该点能被所有的点可达。那么该点所代表的连通分量上的所有的原图中的点,都能被原图中的所有点可达,则该连通分量的点数,就是答案。
4. DAG上面如果有不止一个出度为0的点,则这些点互相不可达,原问题无解,答案为0。
缩点的时候不一定要构造新图,只要把不同强连通分量的点染不同颜色,然后考察各种颜色的点有没有连到别的颜色的边即可(即其对应的缩点后的DAG图上的点是否有出边)。
ps:
有向无环图中唯一出度为0的点,一定可以由任何点出发均可达.
(由于无环,所以从任何点出发往前走,必然终止于一个出度为0的点)
AC代码:
poj1236
题目大意:N个学校之间有单向的网络,每个学校得到一套软件后,可以通过单向网络向周边的学校传输,问题1:初始至少需要向多少个学校发放软件,使得网络内所有的学校最终都能得到软件。 2,至少需要添加几条传输线路(边),使任意向一个学校发放软件后,经过若干次传送,网络内所有的学校最终都能得到软件。
问题可以转化成给定一个有向图,求:
1) 至少要选几个顶点,才能做到从这些顶点出发,可以到达全部顶点
2) 至少要加多少条边,才能使得从任何一个顶点出发,都能到达全部顶点
ps:
有向无环图中所有入度不为0的点,一定可以由某个入度为0的点出发可达。
(由于无环,所以从任何入度不为0的点往回走,必然终止于一个入度为0的点)
AC代码:
Korasju 和tarjan复杂度都是O(v+e),但是tarjan更常用一点。
procedure Strongly_Connected_Components(G);
begin
1.深度优先遍历G,算出每个结点u的结束时间f[u],起点如何选择无所谓。
2.深度优先遍历G的转置图GT, 选择遍历的起点时,按照结点的结束时间从大到小进行。遍历的过程中,一边遍历, 一边给结点做分类标记,每找到一个新的起点,分类标记值就加1。
3. 第2步中产生的标记值相同的结点构成深度优先森林中的一棵树,也即一个强连通分量。
end.
poj2186.
题意比较简单就不写了。
解题思路:
1. 求出所有强连通分量
2. 每个强连通分量缩成一点,则形成一个有向无环图DAG。
3. DAG上面如果有唯一的出度为0的点,则该点能被所有的点可达。那么该点所代表的连通分量上的所有的原图中的点,都能被原图中的所有点可达,则该连通分量的点数,就是答案。
4. DAG上面如果有不止一个出度为0的点,则这些点互相不可达,原问题无解,答案为0。
缩点的时候不一定要构造新图,只要把不同强连通分量的点染不同颜色,然后考察各种颜色的点有没有连到别的颜色的边即可(即其对应的缩点后的DAG图上的点是否有出边)。
ps:
有向无环图中唯一出度为0的点,一定可以由任何点出发均可达.
(由于无环,所以从任何点出发往前走,必然终止于一个出度为0的点)
AC代码:
#include<iostream> #include<vector> #include<algorithm> #include<queue> #include<stdio.h> #include<string.h> using namespace std; vector<int>G[10001]; vector<int>rG[10002]; vector<int >vs; bool used[10001]; int cmp[10001],a,b; int n,m; void dfs(int v) { used[v]=true; for(int i=0; i<G[v].size(); i++) if(!used[G[v][i]]) dfs(G[v][i]); vs.push_back(v); } void rdfs(int v,int k) { used[v]=true; cmp[v]=k; for(int i=0; i<rG[v].size(); i++) if(!used[rG[v][i]])rdfs(rG[v][i],k); } int scc() { memset(used,0,sizeof(used)); vs.clear(); for(int i=0; i<n; i++) if(!used[i]) dfs(i); memset(used,0,sizeof(used)); int k=0; for(int i=vs.size()-1; i>=0; i--) if(!used[vs[i]]) rdfs(vs[i],k++); return k; } int sloved() { int nn=scc(); int num=0; int u; for(int i=0; i<n; i++) if(cmp[i]==nn-1) { u=i; num++; } memset(used,0,sizeof(used)); rdfs(u,0); for(int i=0;i<n;i++) { if(!used[i]) { num=0; break; } } return num; } int main() { scanf("%d%d",&n,&m); for(int i=0; i<m; i++) { scanf("%d%d",&a,&b); G[a-1].push_back(b-1); rG[b-1].push_back(a-1); } printf("%d\n",sloved()); return 0; }
poj1236
题目大意:N个学校之间有单向的网络,每个学校得到一套软件后,可以通过单向网络向周边的学校传输,问题1:初始至少需要向多少个学校发放软件,使得网络内所有的学校最终都能得到软件。 2,至少需要添加几条传输线路(边),使任意向一个学校发放软件后,经过若干次传送,网络内所有的学校最终都能得到软件。
问题可以转化成给定一个有向图,求:
1) 至少要选几个顶点,才能做到从这些顶点出发,可以到达全部顶点
2) 至少要加多少条边,才能使得从任何一个顶点出发,都能到达全部顶点
ps:
有向无环图中所有入度不为0的点,一定可以由某个入度为0的点出发可达。
(由于无环,所以从任何入度不为0的点往回走,必然终止于一个入度为0的点)
AC代码:
#include<iostream> #include<vector> #include<algorithm> #include<queue> #include<stdio.h> #include<string.h> #include<stack> using namespace std; vector< vector<int> >G; stack<int>S; int low[102],dfn[102],fla[102],inde[102],outde[102],cmp[102]; int in,out,cnt,a,b,time,n; void tarjan(int s) { dfn[s]=cnt++; low[s]=dfn[s]; fla[s]=1;//栈内标记 S.push(s); for(int i=0; i<G[s].size(); i++) { int k=G[s][i]; if(!dfn[k]) { tarjan(k); low[s]=min(low[s],low[k]); } else if(fla[k]&&low[s]>dfn[k]) low[s]=dfn[k]; } if(low[s]==dfn[s]) { time++; int e; for(e=S.top(),S.pop(); e!=s; e=S.top(),S.pop()) { cmp[e]=time; fla[e]=0; } cmp[e]=time; fla[e]=0; } } int sloved() { if(time==1) { in=1; return 0; } for(int i=0; i<=n; i++) for(int j=0; j<G[i].size(); j++) { int k=G[i][j]; if(cmp[i]!=cmp[k]) { outde[cmp[i]]++; inde[cmp[k]]++; } } for(int i=1; i<=time; i++) { if(inde[i]==0) in++; if(outde[i]==0) out++; } return max(in,out); } int main() { while(scanf("%d",&n)==1) { G.clear(); G.resize(n+1); for(int i=1; i<=n; i++) while(scanf("%d",&a)&&a) if(a!=i) G[i].push_back(a); memset(low,0,sizeof(low)); memset(dfn,0,sizeof(dfn)); memset(fla,0,sizeof(fla)); memset(inde,0,sizeof(inde)); memset(outde,0,sizeof(outde)); memset(cmp,0,sizeof(cmp)); cnt=1,time=0,in=0,out=0; for(int i=1; i<=n; i++) if(!dfn[i]) tarjan(i); int k=sloved(); printf("%d\n%d\n",in,k); } return 0; }
Korasju 和tarjan复杂度都是O(v+e),但是tarjan更常用一点。
相关文章推荐
- 表单 获取当前时间
- 自定义view入门
- Java文件解压之TGZ解压
- temp = (temp & 0x55555555) + ((temp & 0xaaaaaaaa) >> 1)
- 数据结构与算法——有1亿个整数,找出最大的1000个,要求时间越短越好,空间占用越少越好
- iOS - UICollectionView学习
- XML详解
- Java文件解压
- 原生js写的复选框的全选、不选、反选
- Python新手初学教程
- DEV下Scheduler Control 的部分操作
- 雅思考试(口语篇)
- JavaSE-方法的参数传递
- Codeforces Gym 100015F Fighting for Triangles 状态压缩DP
- 简单的数据查询
- procedure_yhs_20160116
- 基于小端序列的内存规律
- hdu3282 动态中位数(用堆实现)
- unicode下 LPCTSTR 转换为const char*的方法
- C 基础框架开发