HD2767Proving Equivalences(有向图强连通分量+缩点)
题意:有n个节点的图,现在给出了m个边,问最小加多少边是的图是强连通的
分析:首先找到强连通分量,然后把每一个强连通分量缩成一个点,然后就得到了一个DAG。接下来,设有a个节点(每个节点对应一个强连通分量)的入度为0,b个节点的出度为0,然后取ab最大的就行了
#include <iostream> #include <cstdio> #include <cstring> #include <vector> #include <stack> #include <algorithm> using namespace std; const int Max = 20000 + 5; vector<int> g[Max]; int low[Max], dfn[Max]; stack<int> st; int scc_cnt, dfs_clock, sccno[Max]; //scc_cnt强连通分量个数,sccno[x]表示x属于第几个强连通分量 int in0[Max], out0[Max]; int n, m; int Min(int x, int y) { return x > y ? y : x; } void init() { for(int i = 0; i <= n; i++) g[i].clear(); memset(low, 0, sizeof(low)); memset(dfn, 0, sizeof(dfn)); memset(sccno, 0, sizeof(sccno)); while(!st.empty()) st.pop(); dfs_clock = scc_cnt = 0; } void input() { int u, v; scanf("%d%d", &n, &m); for(int i = 0; i < m; i++) { scanf("%d%d", &u, &v); g[u].push_back(v); } } void dfs(int u) { low[u] = dfn[u] = ++dfs_clock; st.push(u); int len = g[u].size(); for(int i = 0; i < len; i++) { int v = g[u][i]; if(!dfn[v]) { dfs(v); low[u] = Min(low[u], low[v]); } else if(!sccno[v]) { low[u] = Min(low[u], dfn[v]); } } //构成一个环就成了强连通分量了 if(low[u] == dfn[u]) { scc_cnt++; while(!st.empty()) { int x = st.top(); st.pop(); sccno[x] = scc_cnt; if(x == u) break; } } } void solve() { for(int i = 1; i <= n; i++) { if(!dfn[i]) dfs(i); } memset(in0, 0, sizeof(in0)); memset(out0, 0, sizeof(out0)); for(int i = 1; i <= n; i++) { int len = g[i].size(); for(int j = 0; j < len; j++) { int v = g[i][j]; if(sccno[i] != sccno[v]) { out0[sccno[i]]++; in0[sccno[v]]++; } } } int a = 0, b = 0; for(int i = 1; i <= scc_cnt; i++) { if(!in0[i]) a++; if(!out0[i]) b++; } int ans = max(a, b);; if(scc_cnt == 1) //如果本身就是连通的输出0 ans = 0; printf("%d\n", ans); } int main(int argc, char** argv) { int t; scanf("%d", &t); while(t--) { init(); input(); solve(); } return 0; }