http://poj.org/problem?id=1236
Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 12991 | Accepted: 5191 |
Description
Input
Output
Sample Input
5 2 4 3 0 4 5 0 0 0 1 0
Sample Output
1 2
题目大意:
给定一个有向图,求:
1) 至少要选几个顶点,才能做到从这些顶点出
发,可以到达全部顶点
2) 至少要加多少条边,才能使得从任何一个顶
点出发,都能到达全部顶点
顶点数<= 100
有用的定理:
有向无环图中所有入度不为0的点,。一定可以由某个入度为0的点出发可达由于无环,所以从任何入度不为0的点往回走必然终止于一个入度为0的点
解题思路:
1. 求出所有强连通分量
2. 每个强连通分量缩成一点,则形成一个有向无环图DAG。
3. DAG上面有多少个入度为0的顶点,问题1的答案就是多少
在DAG上要加几条边,才能使得DAG变成强连通的,问题2的答案就是多少
加边的方法:要为每个入度为0的点添加入边,为每个出度为0的点添加出边假定有 n 个入度为0的点,m个出度为0的点,max(m,n)就是第二个问题的解(证明难,略)
但是比较好想, 我要让那些入度为0,和 出度为 0,的点变为不为 0,这样我们可以形成完全联通,我们一个入度为 0 的连接一个出度为 0 的,可以消失两对,剩下的,我们每个
再随便加一条边就可以了。
当我们求出强联通分量之后就可以,然后重新构图, 最后得出结果, 有一个答案要特殊判断一下, 因为只有一个强联通分量的时候是不需要多加边,因此要特殊判断一下。
#include <iostream> #include <cstring> #include <cstdio> #include <algorithm> #include <cmath> #include <cstdlib> #include <limits> #include <queue> #include <stack> #include <vector> #include <map> using namespace std; #define N 105 #define INF 0xfffffff #define PI acos (-1.0) #define EPS 1e-8 vector <vector <int> > G; int n, Time, cnt, top; int low[N], dfn[N], Instack[N], Stack[N], ft[N], g[N][N]; void Init (); void tarjan (int u); void solve (); int main () { while (~scanf ("%d", &n)) { int a; Init ();//初始化 for (int i=1; i<=n; i++) { while (scanf ("%d", &a), a) G[i].push_back (a); } solve (); } return 0; } void Init () { G.clear (); G.resize (n+1); Time = cnt = top = 0; memset (low, 0, sizeof (low)); memset (dfn, 0, sizeof (dfn)); memset (Instack, 0, sizeof (Instack)); memset (Stack, 0, sizeof (Stack)); memset (g, 0, sizeof (g)); } void tarjan (int u) { low[u] = dfn[u] = ++Time; Instack[u] = 1; Stack[top++] = u; int len = G[u].size (), v; for (int i=0; i<len; i++) { v = G[u][i]; if (!dfn[v]) { tarjan (v); low[u] = min (low[u], low[v]); } else if (Instack[v]) low[u] = min (low[u], dfn[v]); } if (low[u] == dfn[u]) { do { v = Stack[--top]; Instack[v] = 0; ft[v] = cnt; } while (u != v); cnt++; } } void solve () { int ind[N] = {0}, outd[N] = {0}; int in = 0, out = 0; for (int i=1; i<=n; i++) if (!low[i]) tarjan (i); for (int i=1; i<=n; i++) { int len = G[i].size (), v; for (int j=0; j<len; j++) { v = G[i][j]; g[ft[i]][ft[v]] = 1;//连通的两个点 } } for (int i=0; i<cnt; i++) for (int j=0; j<cnt; j++) { if (i == j) continue; if (g[i][j]) ind[j]++, outd[i]++;//计算每个点的入度和出度 } for (int i=0; i<cnt; i++) { if (!ind[i]) in++;//入度为0 if (!outd[i]) out++;//出度为0 } printf ("%d\n", in); if (cnt == 1) puts ("0");//是一个连通图的时候要特殊处理 else printf ("%d\n", max (in, out)); }