题目:
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=72618#problem/C
题意:
给出n个节点的m条边的有向图,要求添加最少的边使得新的图强连通.
思路:
强连通的部分敲模版即可.找出强连通分量,将每一个强连通缩成点, 得到一个新的有向图,然后求出有a个节点的入度为0, b个节点的出度为0, 答案便是a, b的最大值.
一开始不懂这样的思路, 其实画个图就可以知道了.
AC.
#include <iostream>
#include <cstdio>
#include <cstring>
#include <stack>
#include <vector>
using namespace std;
const int MAX = 5e4+5;
vector<int> G[MAX];
int pre[MAX], lowlink[MAX], sccno[MAX], dfs_clock, scc_cnt;
stack<int> S;
int in0[MAX], out0[MAX];
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++;
while(1) {
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 = 0; i < n; ++i) {
if(!pre[i]) dfs(i);
}
}
int main()
{
//freopen("in", "r", stdin);
int T;
scanf("%d", &T);
while(T--) {
int n, m;
for(int i = 0; i < n; ++i) G[i].clear();
scanf("%d %d", &n, &m);
for(int i = 0; i < m; ++i) {
int u, v;
scanf("%d %d", &u, &v);
u--; v--;
G[u].push_back(v);
}
find_scc(n);
memset(in0, 0, sizeof(in0));
memset(out0, 0, sizeof(out0));
for(int u = 0; u < n; ++u) {
for(int i = 0; i < G[u].size(); ++i) {
int v = G[u][i];
if(sccno[u] != sccno[v]) {
in0[sccno[v]] = out0[sccno[u]] = 1;
}
}
}
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) ans = 0;
printf("%d\n", ans);
}
return 0;
}