题意:给定一个图,问至少加入多少条边能够使这个图强连通。
思路:首先求出这个图的强连通分量。然后把每个强连通分量缩成一个点。那么这个图变成了一个DAG,求出全部点的入度和出度,由于强连通图中每个节点的入度和出度至少为1。那么我们求出入度为零的节点数量和出度为零的节点数量。答案取最大值,由于在一个DAG中加入这么多边一定能够使这个图强连通。注意当这个图本身强连通时要特判一下,答案为零。
#include<cstdio> #include<cstring> #include<cmath> #include<cstdlib> #include<iostream> #include<algorithm> #include<vector> #include<map> #include<queue> #include<stack> #include<string> #include<map> #include<set> #include<ctime> #define eps 1e-6 #define LL long long #define pii (pair<int, int>) //#pragma comment(linker, "/STACK:1024000000,1024000000") using namespace std; const int maxn = 30000; //const int INF = 0x3f3f3f3f; int n, m; //强连通分量 vector<int> G[maxn]; int pre[maxn], lowlink[maxn], sccno[maxn], dfs_clock, scc_cnt; stack<int> S; void dfs(int u) { pre[u] = lowlink[u] = ++dfs_clock; S.push(u); for(int i = 0; i < G[u].size(); i++) { int v = G[u][i]; if(!pre[v]) { dfs(v); lowlink[u] = min(lowlink[u], lowlink[v]); } else if(!sccno[v]) { lowlink[u] = min(lowlink[u], pre[v]); } } if(lowlink[u] == pre[u]) { scc_cnt++; for(;;) { int x = S.top(); S.pop(); sccno[x] = scc_cnt; if(x == u) break; } } } void find_scc(int n) { dfs_clock = scc_cnt = 0; memset(sccno, 0, sizeof(sccno)); memset(pre, 0, sizeof(pre)); for(int i = 1; i <= n; i++) if(!pre[i]) dfs(i); } int in[maxn], out[maxn]; int main() { //freopen("input.txt", "r", stdin); int T; cin >> T; while(T--) { cin >> n >> m; for(int i = 1; i <= n; i++) G[i].clear(); for(int i = 0; i < m; i++) { int u, v; scanf("%d%d", &u, &v); G[u].push_back(v); } find_scc(n); memset(in, 0, sizeof(in)); memset(out, 0, sizeof(out)); for(int i = 1; i <= n; i++) { for(int j = 0; j < G[i].size(); j++) { if(sccno[i] != sccno[G[i][j]]) out[sccno[i]]++, in[sccno[G[i][j]]]++; } } int sin = 0, sout = 0; for(int i = 1; i <= scc_cnt; i++) { if(!in[i]) sin++; if(!out[i]) sout++; } int ans = scc_cnt==1 ? 0 : max(sin, sout); cout << ans << endl; } return 0; }
时间: 2024-11-02 23:33:21