1236:Network of Schools
keyword
有向グラフ 強連結成分分解 C++
概要
ノード数V(<100)の有向グラフが与えられる。このとき、(1)全てのノードに情報を伝達するには最大でいくつのノードに情報を流せばよいか。(2)任意の点に情報を与えたとき全てのノードに情報が伝わるようにしたい、最小で何本の辺を追加すればよいか。をそれぞれ求める問題。
なにはともあれ強連結成分分解してDAGに落とす。落とした上で、(1)はDAGのsourceの個数であり、(2)はそのDAGを一つの強連結成分にするのに必要な辺の最小化、とそれぞれ読み替える。(1)は簡単。(2)は、まず連結成分を環状につなぎ、あまったsourceとsinkをつなげばよい。なので、答えはsource、sink、連結成分の個数をそれぞれA,B,Cとすると、C+max(A-C, B-C) = max(A, B)。ただし強連結成分が一つしかないときは例外なので注意。計算量O(V+E)。ソースには余計なものが入っています。
vector<int> graph[102]; vector<int> revGraph[102]; bool visited[102]; int cmp[102]; int cmp2[102]; vector<int> st; int N; void dfs2(int v){ visited[v] = true; for(int i=0; i<graph[v].size(); i++){ int u = graph[v][i]; if(!visited[u]) dfs2(u); } for(int i=0; i<revGraph[v].size(); i++){ int u = revGraph[v][i]; if(!visited[u]) dfs2(u); } } void dfs(int v){ visited[v] = true; for(int i=0; i<graph[v].size(); i++){ int u = graph[v][i]; if(!visited[u]) dfs(u); } st.push_back(v); } void revDfs(int v, int k){ visited[v] = true; for(int i=0; i<revGraph[v].size(); i++){ int u = revGraph[v][i]; if(!visited[u]) revDfs(u, k); } if(k>=0) cmp[v] = k; } int SCC(){ memset(visited, 0, sizeof(visited)); for(int i=0; i<N; i++)if(!visited[i]) dfs(i); memset(visited, 0, sizeof(visited)); int ret = 0; while(!st.empty()){ int v = st.back(); st.pop_back(); if(!visited[v]){ revDfs(v,ret); ret++; } } return ret; } void solve(){ int k = SCC(); vector<int> vs(k); vector<int> isSource(k, false), isSink(k,false); int ansA = 0, ansB = 0; for(int i=0; i<N; i++){ vs[cmp[i]] = i; } memset(visited, 0, sizeof(visited)); for(int i=0; i<k; i++)if(!visited[vs[i]]){ dfs(vs[i]); isSource[i] = true; } memset(visited, 0, sizeof(visited)); for(int i=k-1; i>=0; i--)if(!visited[vs[i]]){ revDfs(vs[i],-1); isSink[i] = true; } int cnt = 0; memset(visited, 0, sizeof(visited)); for(int i=0; i<k; i++)if(!visited[vs[i]]){ dfs2(vs[i]); cnt++; } for(int i=0; i<k; i++)if(isSource[i]) ansA++; if(k!=1){ for(int i=0; i<k; i++)if(isSink[i])ansB++; ansB = max(ansA, ansB); } printf("%d\n%d\n",ansA, ansB); }