一些学校连入一个电脑网络。那些学校已订立了协议:每个学校都会给其它的一些学校分发软件(称作“接受学校”)。
注意即使 B 在 A学校的分发列表中,A 也不一定在 B 学校的列表中。
你要写一个程序计算,根据协议,为了让网络中所有的学校都用上新软件,必须接受新软件副本的最少学校数目(子任务 A)。
更进一步,我们想要确定通过给任意一个学校发送新软件,这个软件就会分发到网络中的所有学校。为了完成这个任务,我们
可能必须扩展接收学校列表,使其加入新成员。计算最少需要增加几个扩展,使得不论我们给哪个学校发送新软件,它都会到达其余所有的学校(子任务 B)。一个扩展就是在一个学校的接收学校列表中引入一个新成员。
一个强联通分量的模板题 首先第一问的答案显然是缩点后出度为零的点的个数(起点);
第二问答案可以大胆猜测 得到其为 max(起点数加终点数)
$$ f(x) = a+b $$
假设起点数为P,终点数为Q 且P <= Q,那么我们只需要找到一个最长的起点并把各个终点向它连边即可,Q <= P 同理可得
#include <iostream> #include <algorithm> #include <cstdio> #include <cstring> using namespace std; int n; const int N = 110; const int M = 10010; int h[N],e[M],ne[M],idx = 0; int low[N],dfn[N],id[N],din[N],dout[N]; int timestamp = 0,scc_cnt = 0; bool is_stk[N]; int tot = 0,stk[N]; void add(int u,int v) { e[idx] = v; ne[idx] = h[u]; h[u] = idx ++; } void tarjan(int u) { low[u] = dfn[u] = ++ timestamp; stk[++ tot] = u; is_stk[u] = true; for(int i = h[u]; ~i; i = ne[i]) { int j = e[i]; if(!dfn[j]) { tarjan(j); low[u] = min(low[u],low[j]); } else if(is_stk[j]) low[u] = min(low[u],dfn[j]); } if(low[u] == dfn[u]) { int y; scc_cnt ++; do { y = stk[tot --]; is_stk[y] = false; id[y] = scc_cnt; } while (y != u); } } int main() { memset(h,-1,sizeof h); scanf("%d",&n); for(register int i = 1; i <= n; ++ i) { int x; while(cin >> x,x) { add(i,x); } } for(register int i = 1; i <= n; ++ i) if(!dfn[i]) tarjan(i); for (int i = 1; i <= n; i ++ ) for (int j = h[i]; ~j; j = ne[j]) { int k = e[j]; int a = id[i], b = id[k]; if (a != b) dout[a] ++,din[b] ++; } int ans = 0,res = 0; for(register int i = 1; i <= scc_cnt; ++ i) { if(!din[i]) ans ++; if(!dout[i]) res ++; } printf("%d\n",ans); printf("%d\n",scc_cnt == 1 ? 0 : max(ans,res)); return 0; }