有向图强连通分量
tarjan 算法模板
#include
using namespace std;
const int N = 110, M = 10010;
int n;
int h[N], e[M], ne[M], idx;
int low[N], dfn[N], din[N], dout[N], timestamp, top;
int stk[N], id[N], dcc_cnt;
bool is_instk[N];
void add(int a, int b)
{
e[idx] = b; ne[idx] = h[a]; h[a] = idx ++;
}
void tarjan(int u)
{
low[u] = dfn[u] = ++timestamp;
stk[++top] = u;
is_instk[u] = true;
for(int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if(!dfn[j])
{
tarjan(j);
low[u] = min(low[u], low[j]);
}
else if(is_instk[j])
{
low[u] = min(low[u], dfn[j]);
}
}
if(low[u] == dfn[u])
{
dcc_cnt ++;
int y;
do{
y = stk[top --];
id[y] = dcc_cnt;
is_instk[y] = false;
}while(y != u);
}
}
int main()
{
int n; cin >> n;
memset(h, -1, sizeof(h));
for(int i = 1; i <= n; i ++)
{
int x;
while(cin >> x, x)
{
add(i, x);
}
}
for(int i = 1; i <= n; i ++)
{
if(!dfn[i])
tarjan(i);
}
for(int u = 1; u <= n; u ++)
{
for(int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if(id[j] != id[u])
{
dout[id[u]] ++;
din[id[j]] ++;
}
}
}
int a = 0, b = 0;
for(int i = 1; i <= dcc_cnt; i ++)
{
if(!din[i]) a++;
if(!dout[i]) b ++;
}
cout << a << endl;
if(dcc_cnt == 1) cout << 0 << endl;
else cout << max(a, b) << endl;
return 0;
}

- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
结论:
- 有向图最少需要加min(in, out) 边变为强连通分量
in是入度为0的连通分量的个数
out是出度为0的连通分量的个数
无向图双连通分量
无向图边的双连通分量(桥)
#include
using namespace std;
const int N = 5100, M = 20100;
int n, m;
int h[N], e[M], ne[M], idx;
int low[N], dfn[N], stk[N], d[N], id[N], top, timestamp, dcc_cnt;
bool is_bridge[M];
void add(int a, int b)
{
e[idx] = b;
ne[idx] = h[a];
h[a] = idx ++;
}
void tarjan(int u, int from)
{
dfn[u] = low[u] = ++ timestamp;
stk[++top] = u;
for(int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if(!dfn[j])
{
tarjan(j, i);
low[u] = min(low[u], low[j]);
if(dfn[u] < low[j])
{
is_bridge[i] = is_bridge[i^1] = true;
}
}
else if(i != (from ^ 1))
{
low[u] = min(low[u], dfn[j]);
}
}
if(low[u] == dfn[u])
{
int y; ++dcc_cnt;
do{
y = stk[top--];
id[y] = dcc_cnt;
}while(y != u);
}
}
int main()
{
cin >> n >> m;
memset(h, -1, sizeof(h));
for(int i = 1; i <= m; i ++)
{
int a, b; cin >> a >> b;
add(a, b), add(b, a);
}
tarjan(1, -1);
for(int i = 0; i < idx; i ++)
{
if(is_bridge[i])
{
d[id[e[i]]] ++;
}
}
int ans = 0;
for(int i = 1; i <= dcc_cnt; i ++)
{
if(d[i] == 1)
{
ans ++;
}
}
cout << (ans+1)/2 << endl;
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
结论
- 把无向图变为边双连通无向图最少加缩点后叶子节点除2上取整
res = (ans+1)/2 叶子节点就是缩点后度数唯一的连通分量