题意:学校有一些单向网络,现在需要传一些文件
求:1,求最少需要向几个学校分发文件才能让每个学校都收到,
2,需要添加几条网络才能从任意一个学校分发都可以传遍所有学校。
解题思路(参考大神的):
1. 求出所有强连通分量
2. 每个强连通分量缩成一点,则形成一个有向无环图DAG。
3. DAG上面有多少个入度为0的顶点,问题1的答案就是多少
在DAG上要加几条边,才能使得DAG变成强连通的,问题2的答案就是多少
加边的方法:
要为每个入度为0的点添加入边,为每个出度为0的点添加出边
假定有 n 个入度为0的点,m个出度为0的点,如何加边?
把所有入度为0的点编号 0,1,2,3,4 ....N -1
每次为一个编号为i的入度0点可达的出度0点,添加一条出边,连到编号为(i+1)%N 的那个出度0点,
这需要加n条边
若 m <= n,则
加了这n条边后,已经没有入度0点,则问题解决,一共加了n条边
若 m > n,则还有m-n个入度0点,则从这些点以外任取一点,和这些点都连上边,即可,这还需加m-n条边。
所以,max(m,n)就是第二个问题的解
此外:当只有一个强连通分支的时候,就是缩点后只有一个点,虽然入度出度为0的都有一个,但是实际上不需要增加清单的项了,所以答案是1,0;
#include <iostream> #include <cstdlib> #include <cstdio> #include <algorithm> #include <vector> #include <queue> #include <cmath> #include <cstring> #include <stack> using namespace std; #define INF 0xfffffff #define N 10060 int Out[N],In[N], n; int low[N], dfn[N], num, cnt, Time, vis[N], belong[N]; vector<int> G[N]; stack<int>sta; void Init() { Time=num=cnt=0; for(int i=0;i<=n;i++) G[i].clear(); memset(dfn, 0, sizeof(dfn)); memset(low, 0, sizeof(low)); memset(vis, 0, sizeof(vis)); memset(belong, 0, sizeof(belong)); memset(Out, 0, sizeof(Out)); memset(In, 0, sizeof(In)); } void Tarjan(int u) { dfn[u] = low[u] = ++Time; int len = G[u].size(), v; sta.push(u); vis[u] = 1; for(int i=0; i<len; i++) { v = G[u][i]; if(dfn[v]==0) { Tarjan(v); low[u] = min(low[u], low[v]); } else if(vis[v]==1) { low[u] = min(dfn[v], low[u]); } } if(low[u]==dfn[u]) { ++num; do { v = sta.top(); belong[v] = num;// 缩点; sta.pop(); vis[v] = 0; }while(u!=v); } } int main() { int a, ans1, ans2, ans, u, v; while(scanf("%d", &n)!=EOF) { Init(); for(int i=1; i<=n; i++) { while(scanf("%d", &a), a) { G[i].push_back(a); } } for(int i=1; i<=n; i++) if(!dfn[i]) Tarjan(i); ans1 = ans2 = 0; for(int i=1; i<=n; i++) { int len=G[i].size(); for(int j=0; j<len; j++) { u = belong[i]; v = belong[G[i][j]]; if(u!=v) { Out[u]++; In[v]++; } } } for(int i=1; i<=num; i++) { if(Out[i]==0)ans2++; if(In[i]==0)ans1++; } ans = max(ans1, ans2); if(num==1) printf("1 0 "); else printf("%d %d ", ans1, ans); } return 0; }