Network of Schools 题目背景 poj1236
题目描述 大致意思: $N(2 < N < 100)$ 各学校之间有单向的网络,每个学校得到一套软件后,可以通过单向网络向周边的学校传输。
问题:初始至少需要向多少个学校发放软件,使得网络内所有的学校最终都能得到软件。至少需要添加几条传输线路(边),使任意向一个学校发放软件后,经过若干次传送,网络内所有的学校最终都能得到软件。
输入格式 The first line contains an integer N: the number of schools in the network (2 <= N <= 100). The schools are identified by the first N positive integers. Each of the next N lines describes a list of receivers. The line i+1 contains the identifiers of the receivers of school i. Each list ends with a 0. An empty list contains a 0 alone in the line.
输出格式 Your program should write two lines to the standard output. The first line should contain one positive integer: the solution of subtask A. The second line should contain the solution of subtask B.
样例数据 1 输入
输出
分析 题目理解 给定一个有向图,求:
至少要选几个顶点,才能做到从这些顶点出发,可以到达全部顶点。
至少要加多少条边,才能使得从任何一个顶点出发,都能到达全部顶点。
思路
求出所有强连通分量。
每个强连通分量缩成一点,则形成一个有向无环图DAG。
DAG上面有多少个入度为0的顶点,问题1的答案就是多少。
在DAG上要加几条边,才能使得DAG变成强连通的,问题2的答案就是多少。
方法 要为每个入度为 $0$ 的点添加入边,为每个出度为 $0$ 的点添加出边。假定有 $n$ 个入度为 $0$ 的点,$m$ 个出度为 $0$ 的点,把所有入度为 $0$ 的点编号 $0,1,2,3,4,\cdots, N-1$,每次为一个编号为i的入度 $0$ 点可达的出度 $0$ 点,添加一条出边,连到编号为 $(i+1) % N$ 的那个出度 $0$点,这需要加 $n$ 条边。
若 $m \leq n$,则加了这 $n$ 条边后,已经没有入度 $0$ 点,则问题解决,一共加了 $n$ 条边。
若 $m > n$,则还有 $m - n$ 个入度 $0$ 点,则从这些点以外任取一点,和这些点都连上边,即可,这还需加 $m - n$ 条边。
所以,$max(m,n)$ 就是第二个问题的解。
此外: 当只有一个强连通分支的时候,就是缩点后只有一个点,虽然入度出度为0的都有一个,但是实际上不需要增加清单的项了,所以答案是 $1, 0$
源码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 #include <cstring> #include <iostream> #include <stack> #include <vector> using namespace std ;char ch_buffer;bool signum;inline void readInt (int &l) { l = 0 ; do ch_buffer = getchar(); while ((ch_buffer < '0' || ch_buffer > '9' ) && ch_buffer != '0' && ch_buffer != '-' ); if (ch_buffer == '-' ) ch_buffer = getchar(), signum = true ; while (ch_buffer <= '9' && ch_buffer >= '0' ) l = (l << 3 ) + (l << 1 ) + ch_buffer - '0' , ch_buffer = getchar(); if (signum) l = -l, signum = false ; } #define M 110 stack <int > st; int DFN[M]; int Low[M]; int ComponentNumber = 0 ; int Index = 0 ; vector <int > Edge[M]; vector <int > Component[M]; int InComponent[M]; int ComponentDegree[M]; int inDegree[M]; int outDegree[M]; int n;inline void insertMulty (int u, int v) { Edge[u].push_back(v), Edge[v].push_back(u); } inline void insert (int u, int v) { Edge[u].push_back(v); }inline void init (int num_of_n) { memset (DFN, 0 , sizeof (DFN)); memset (Low, 0 , sizeof (Low)); memset (inDegree, 0 , sizeof (inDegree)); memset (outDegree, 0 , sizeof (outDegree)); while (!st.empty()) st.pop(); ComponentNumber = Index = 0 ; n = num_of_n; } void tarjan (int u) { DFN[u] = Low[u] = ++Index; st.push(u); int v; for (int e = 0 ; e < Edge[u].size(); e++) { v = Edge[u][e]; if (!DFN[v]) { tarjan(v); Low[u] = min(Low[u], Low[v]); } else if (!InComponent[v]) Low[u] = min(Low[u], Low[v]); } if (DFN[u] == Low[u]) { ComponentNumber++; do { v = st.top(), st.pop(); Component[ComponentNumber].push_back(v); InComponent[v] = ComponentNumber; } while (v != u); } } inline void degree () { for (int i = 0 ; i < n; i++) { for (int j = 0 ; j < Edge[i].size(); j++) { int k = Edge[i][j]; if (InComponent[i] != InComponent[k]) { inDegree[InComponent[k]]++; outDegree[InComponent[i]]++; } } } } int x;int main (int argc, char const *argv[]) { readInt(n); init(n); for (int i = 0 ; i < n; i++) { while (readInt(x), x) x--, insert(i, x); } for (int i = 0 ; i < n; i++) if (!DFN[i]) tarjan(i); if (ComponentNumber == 1 ) cout << "1\n0\n" , exit (0 ); degree(); int in_tot = 0 , out_tot = 0 ; for (int i = 1 ; i <= ComponentNumber; i++) { if (!inDegree[i]) in_tot++; if (!outDegree[i]) out_tot++; } cout << in_tot << "\n" << max(in_tot, out_tot); return 0 ; }