POJ1236_A - Network of Schools _强连通分量::Tarjan算法
2017-07-15 14:35
429 查看
题意
有向图上有 N 个点,若干有向边。第一问:至少给几个点传递信息,才能保证信息传遍整个图。
第二问:至少添加几条边,才能使任意选择点,都能传遍整个图。
思路
强连通分量的裸题。强连通分量内的任意一点收到消息,内部其他各点必定都能收到消息。因此,可以把每个强连通分量缩成一个点。只需要考察入度为 0 的强连通分量的个数,就是第一问的答案。
对于第二问,是把图连接成一个强连通分量,同样可以在缩点后的图中操作。这里的做法是统计图中入度为0、出度为0的强连通分量的个数,取较大值即为第二问的答案。
原图中各ssc之间不会成环,如果成环的话就是一个ssc了。要使整个图强连通,至少要使所有分量的入度和出度都为0。这样也是必定存在合法方案的。
本题中原图只有一个强连通分量的情况需要特判。
Tarjan算法
Tarjan算法是基于bfs查找强连通分量的方法。每个点赋予三个附加属性。时间戳,发现该点的时间;根,该节点所在强连通分量中的最小时间戳;是否在栈中标记。
从任意一点出发,对图进行bfs。
每进入一个点,标记时间戳,将根置为本身,压栈。然后搜索与它相连的其他各点。如果没有到达过,则进入那个点。如果已经到达过,并且那个点现在仍在栈中,说明这两个点在同一个强连通分量里。比较当前点的根和那个点的时间戳,如果那个点的时间戳较小,则把根改成那个点的时间戳。即这两个点并入了同一个强连通分量。
沿bfs路径返回的时候,首先更新根节点(因为出现环表示发现了强连通分量,而从下网上返回的时候靠下的点的根是真的根),并检查每个点的时间戳和根是否相等。如果相等,说明当前点就是它所在强连通分量的根。而此时的栈中,比它后进栈的元素都在这个强连通分量中。更新ssc个数,将这些点连通当前点加入一个ssc中,并全部弹栈。
题目链接
http://poj.org/problem?id=1236AC代码
#include<iostream> #include<cstdio> #include<cstring> #include<vector> using namespace std; const int maxn = 100 + 10; int N; int In[maxn], Out[maxn]; /***************************Tarjan算法模板***************************/ vector<int> G[maxn]; int Mark[maxn], Root[maxn], Stack[maxn]; //时间戳,根(当前分量中时间戳最小的节点),栈 bool Instack[maxn]; //是否在栈中标记 int Ssc[maxn]; //每个节点所在的强连通分量的编号 int Index, Ssc_n, Top; //搜索时用的时间戳,强连通分量总数,栈顶指针 void Tarjan(int u) //u 当前搜索到的点 { Mark[u] = Root[u] = ++ Index; //每找到一个点,对时间戳和根初始化 Stack[Top ++] = u; //压栈 Instack[u] = true; //在栈中标记 int v; for(int i= 0; i< G[u].size(); i++) //向下搜索 { v = G[u][i]; if(Mark[v] == 0) //没到过的点 { Tarjan(v); //先向下搜索 if(Root[u] > Root[v]) Root[u] = Root[v]; c8cd //更新根 } else if(Instack[v] && Root[u] > Mark[v]) Root[u] = Mark[v]; //到过的点且点仍在栈中,试着看这个点能不能成为根 } /*对当前点的搜索结束*/ if(Mark[u] == Root[u]) //当前点本身时根 { Ssc_n ++; //更新强连通分量数 do{ //栈中比它后入栈的元素在以它为根的强连通分量中 v = Stack[-- Top]; Instack[v] = false; Ssc[v] = Ssc_n; }while(v != u); //直到它自己 } } void SSC() { memset(Mark, 0, sizeof Mark); //初始化时间戳和栈内标记 memset(Instack, false, sizeof Instack); Index = Ssc_n = Top = 0; //初始化时间戳,强连通分量数,栈顶指针 for(int i= 1; i<= N; i++) //保证图上所有点都访问到 if(Mark[i] == 0) Tarjan(i); } /***************************Tarjan算法模板***************************/ int main() { //freopen("in.txt", "r", stdin); scanf("%d", &N); for(int i= 1; i<= N; i++) { int x; while(scanf("%d", &x), x) G[i].push_back(x); } SSC(); if(Ssc_n == 1) //只有一个强连通分量的情况 { cout << "1\n0\n"; return 0; } memset(In, 0, sizeof In); //求每个强连通分量的入度和出度 memset(Out, 0, sizeof Out); for(int u= 1; u<= N; u++) { for(int i= 0; i< G[u].size(); i++) { int v = G[u][i]; if(Ssc[u] != Ssc[v]) Out[Ssc[u]] ++, In[Ssc[v]] ++; } } int S1 = 0, S2 = 0; //找入度为0、出度为0的点的数目 for(int i= 1; i<= Ssc_n; i++) { if(In[i] == 0) S1 ++; if(Out[i] == 0) S2 ++; } cout << S1 << endl << max(S1, S2) << endl; return 0; }
相关文章推荐
- POJ1236 Network of Schools(图的强连通分量,Tarjan算法)
- poj1236 - Network of Schools(连通图--tarjan算法)
- poj1236 Network of Schools 【连通图-强联通分量-tarjan】
- POJ 1236 Network of Schools[连通分量]
- Network of Schools POJ - 1236 tarjan强连通分量缩点
- POJ 1236 Network Of Schools ( tarjan求强连通分量 + 缩点成DAG图 )
- Network of Schools hoj&poj 强连通分量的应用 经典题!
- POJ1236---Network of Schools (强连通分量,缩点,Tarjan算法)
- [ACM] poj 1236 Network of Schools (有向强连通分量)
- 强连通分量,Tarjan,缩点(Network of Schools,POJ 1236)
- POJ1236 Network of Schools【强连通】
- 【强连通分量缩点】poj 1236 Network of Schools
- 【POJ1236】Network of Schools 强联通分量缩点(本文kosaraju)
- poj1236 Network of Schools(强连通-缩点)
- 【POJ 1236 Network of Schools】强联通分量问题 Tarjan算法,缩点
- poj1236 Network of Schools ,有向图求强连通分量(Tarjan算法),缩点
- USACO Section 5.3 Network of Schools - 回顾Tarjan求强连通分量
- poj1236 Network of Schools ,有向图求强连通分量(Tarjan算法),缩点
- POJ 1236 Network of Schools【强连通缩点】【Tarjan算法】
- POJ 1236 Network of Schools 有向图强连通分量