传送门

 

题意:

  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 }
View Code

相关文章: