题目大意:
给定一个有向图,求:
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 <cstdlib> #include <cstdio> #include <algorithm> #include <vector> #include <queue> #include <cmath> #include <stack> #include <cstring> usingnamespace std; #define INF 0xfffffff #define maxn 105 #define min(a,b) (a<b?a:b) bool InStack[maxn]; int low[maxn], dfn[maxn], Stack[maxn], top, time, cnt; int maps[maxn][maxn]; int belong[maxn]; int n; vector<int> G[maxn]; void Init() { memset(low, 0, sizeof(low)); memset(dfn, 0, sizeof(dfn)); memset(InStack, false, sizeof(InStack)); memset(maps, 0, sizeof(maps)); cnt = time = top = 0; for(int i=0; i<=n; i++) G[i].clear(); } void Tarjan(int u) { low[u] = dfn[u] = ++time; Stack[top++] = u; InStack[u] = true; int len = G[u].size(), v; for(int i=0; i<len; i++) { v = G[u][i]; if( !low[v] ) { Tarjan(v); low[u] = min(low[u], low[v]); } elseif( InStack[v] ) { low[u] = min(low[u], dfn[v]); } } if(low[u] == dfn[u]) { do { v = Stack[--top]; belong[v] = cnt; InStack[v] = false; }while(u != v); cnt ++; } } void solve() { int InDegree[maxn] = {0}; int OutDegree[maxn] = {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]; maps[belong[i] ][belong[v] ] = 1; } } for(int i=0; i<cnt; i++) { for(int j=0; j<cnt; j++) { if( i == j) continue; if(maps[i][j]) { InDegree[j] ++; OutDegree[i] ++; } } } for(int i=0; i<cnt; i++) { if(InDegree[i] == 0) In ++; if(OutDegree[i] == 0) Out ++; } printf("%d ", In); if(cnt == 1) printf("0 "); else printf("%d ", max(In, Out)); } int main() { while(scanf("%d", &n) != EOF) { Init(); for(int i=1; i<=n; i++) { int a; while(scanf("%d",&a), a) G[i].push_back(a); } solve(); } return0; }