题意:
N(2<N<100)个学校之间有单向的网络,每个学校得到一套软件后,可以通过单向网络向周边的学校传输。
问题1:初始至少需要向多少个学校发放软件,使得网络内所有的学校最终都能得到软件。
问题2:至少需要添加几条传输线路(边),使任意向一个学校发放软件后,经过若干次传送,网络内所有的学校最终都能得到软件。
从题意中抽象出的算法模型:
给定一个有向图,求:
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的点,添加一条出边,这需要加n条边。
若 m <= n,则
加了这n条边后,已经没有入度0点,则问题解决,一共加了 n 条边
若 m > n,则还有m-n个入度0点,则从这些点以外任取一点,和这些点都连上边,即可,这还需加m-n条边。
所以,max(m,n)就是第二个问题的解
此外:当只有一个强连通分支的时候,就是缩点后只有一个点,虽然入度出度为0的都有一个,但是实际上不需要增加边了,所以答案是0。
以上解析来源于bin巨%%%%%%
AC代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<vector> 5 using namespace std; 6 #define pb push_back 7 #define mem(a,b) memset(a,b,sizeof a) 8 const int maxn=110;//最大的节点个数,一般是 1e5 级别的 9 10 int scc[maxn];//所属强连通分量的拓扑排序 11 bool vis[maxn];//vis[u] : dfs中判断节点u是否被访问过 12 vector<int >vs;//后序遍历顺序的顶点列表 13 vector<int >edge[maxn],redge[maxn];//边、反边 14 15 void addEdge(int u,int v) 16 { 17 edge[u].pb(v); 18 redge[v].pb(u); 19 } 20 void Dfs(int u)//第一次dfs,后序遍历标记,越靠近叶子结点标号越小 21 { 22 vis[u]=true; 23 for(int i=0;i < edge[u].size();++i) 24 { 25 int to=edge[u][i]; 26 if(!vis[to]) 27 Dfs(to); 28 } 29 vs.pb(u); 30 } 31 void rDfs(int u,int sccId)//反向dfs,利用反向图,求出强连通分量个数 32 { 33 vis[u]=true; 34 scc[u]=sccId; 35 for(int i=0;i < redge[u].size();++i) 36 { 37 int to=redge[u][i]; 38 if(!vis[to]) 39 rDfs(to,sccId); 40 } 41 } 42 int Scc(int maxV) 43 { 44 mem(vis,false); 45 vs.clear(); 46 for(int i=1;i <= maxV;++i) 47 if(!vis[i]) 48 Dfs(i); 49 mem(vis,false); 50 int sccId=0;//DAG节点个数 51 for(int i=vs.size()-1;i >= 0;--i) 52 { 53 int to=vs[i]; 54 if(!vis[to]) 55 { 56 sccId++; 57 rDfs(to,sccId); 58 } 59 } 60 return sccId;//返回强连通分量的个数 61 } 62 int main() 63 { 64 int N; 65 scanf("%d",&N); 66 for(int i=1;i <= N;++i) 67 { 68 int v; 69 while(scanf("%d",&v) && v) 70 addEdge(i,v); 71 } 72 int sccId=Scc(N); 73 int in[maxn]; 74 int out[maxn]; 75 mem(in,0); 76 mem(out,0); 77 78 for(int i=1;i <= N;++i) 79 for(int j=0;j < edge[i].size();++j) 80 { 81 int to=edge[i][j]; 82 if(scc[i] != scc[to]) 83 out[scc[i]]++,in[scc[to]]++; 84 } 85 int zeroIn=0; 86 int zeroOut=0; 87 for(int i=1;i <= sccId;++i) 88 { 89 zeroIn += (in[i] == 0 ? 1:0); 90 zeroOut += (out[i] == 0 ? 1:0); 91 } 92 printf("%d\n",zeroIn); 93 if(sccId == 1) 94 printf("0\n"); 95 else 96 printf("%d\n",max(zeroIn,zeroOut)); 97 }