题意:给出一个有向图,问最少添加几条有向边使得原图强连通。
解法:求出SCC后缩点,统计一下出度为0的点和入度为0的点,二者取最大值就是答案。
还有个特殊情况就是本身就是强连通的话,答案就是0.
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <stack>
using namespace std; const int maxn = + ; int n, m; vector<int> G[maxn]; stack<int> S;
int pre[maxn], low[maxn], sccno[maxn];
int dfs_clock, scc_cnt; void dfs(int u, int fa)
{
low[u] = pre[u] = ++dfs_clock;
S.push(u); for(int i = ; i < G[u].size(); i++)
{
int v = G[u][i];
if(!pre[v])
{
dfs(v, u);
low[u] = min(low[u], low[v]);
}
else if(!sccno[v]) low[u] = min(low[u], pre[v]);
} if(pre[u] == low[u])
{
scc_cnt++;
for(;;)
{
int x = S.top(); S.pop();
sccno[x] = scc_cnt;
if(x == u) break;
}
}
} void find_scc()
{
memset(pre, , sizeof(pre));
memset(sccno, , sizeof(sccno));
dfs_clock = scc_cnt = ;
for(int i = ; i <= n; i++) if(!pre[i]) dfs(i, );
} int in[maxn], out[maxn]; int main()
{
int T; scanf("%d", &T);
while(T--)
{
scanf("%d%d", &n, &m);
for(int i = ; i <= n; i++) G[i].clear();
while(m--)
{
int u, v; scanf("%d%d", &u, &v);
G[u].push_back(v);
} find_scc(); if(scc_cnt == ) { puts(""); continue; } memset(in, , sizeof(in));
memset(out, , sizeof(out));
for(int i = ; i <= n; i++)
for(int j = ; j < G[i].size(); j++)
{
int u = sccno[i], v = sccno[G[i][j]];
if(u != v) { out[u]++; in[v]++; }
} int hehe = , haha = ;
for(int i = ; i <= scc_cnt; i++)
{
if(!in[i]) hehe++;
if(!out[i]) haha++;
}
printf("%d\n", max(hehe, haha));
} return ;
}
代码君