有向图强连通分量
tarjan 算法模板
cpp
#include<bits/stdc++.h>
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;
}
结论:
- 有向图最少需要加min(in, out) 边变为强连通分量
in是入度为0的连通分量的个数
out是出度为0的连通分量的个数
无向图双连通分量
无向图边的双连通分量(桥)
cpp
#include<bits/stdc++.h>
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;
}
结论
- 把无向图变为边双连通无向图最少加缩点后叶子节点除2上取整
res = (ans+1)/2 叶子节点就是缩点后度数唯一的连通分量