题意:
一个有向图,问最少加几条边,能让它强连通
方法:
1:tarjan 缩点
2:采用如下构造法:
缩点后的图找到所有头结点和尾结点,那么,可以这么构造:把所有的尾结点连一条边到头结点,就必然可以是强连通了。如果说有几个结点不连通,那么让他们的尾结点相互只向对方的头结点就好了。
那么,最后的答案就是,头结点和尾结点中比较小的那个数量。
当然,如果缩点后只有一个点,那么就是0;
代码:
#include <cstdio> #include <cstring> #include <vector> #include <algorithm> using namespace std; #define N 20010 using namespace std; vector <int> to[N]; vector <int> g[N]; int in[N], out[N]; //#define vii vector<int>iterator; int low[N], dep[N], id[N], s[N], top, scnt, cnt; int n, m; void tarinit() { top = cnt = scnt = 0; memset(dep, -1, sizeof(dep)); } void tarjan(int u) { int minc = low[u] = dep[u] = cnt++; s[top++] = u; int end = to[u].size(); for (int i = 0; i < end; i++) { if (dep[to[u][i]] == -1) tarjan(to[u][i]); if (minc > low[to[u][i]]) minc = low[to[u][i]]; } if (minc < low[u]) low[u] = minc; else { while (s[top] != u){ id[s[--top]] = scnt; low[s[top]] = n+1; } scnt++; } } int main() { int t; scanf("%d", &t); while (t--) { scanf("%d%d", &n, &m); for (int i = 0; i <= n; i++) to[i].clear(), g[i].clear(); for (int i = 0; i < m; i++) { int u, v; scanf("%d%d", &u, &v); to[u].push_back(v); } tarinit(); for (int i = 1; i <= n; i++) { if (dep[i] == -1) { tarjan(i); } } memset(out,0,sizeof(out)); memset(in,0,sizeof(in)); for (int i = 1; i <= n; i++) { int end = to[i].size(); for (int j = 0; j < end; j++) { if (id[i] != id[to[i][j]]) { out[id[i]]++; in[id[to[i][j]]]++; } } } if (scnt == 1) { puts("0"); continue; } int root = 0; int leaf = 0; for (int i = 0; i < scnt; i++) { if (out[i] == 0) leaf++; if (in[i] == 0) root++; } printf("%d ", max(root,leaf)); } return 0; }