割边:类似于割点连接图的部分,若去掉割边图就会被分割为两部分。
判断条件:一条边(u,v)是桥当且仅当满足(u,v)为树枝边,且Low[v]>DFN[u]。
注意:由于是无向边,要排除掉返祖边的影响(即一个点的字节点不能是父节点)。
code:
#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<cmath>
#include<map>
using namespace std;
const int maxn = 1e5 + 5;
struct edge{
int v, next;
}edges[maxn * 10];
int head[maxn], cot;
int dfn[maxn], low[maxn], cnt;
pair<int, int> p[maxn]; //记录割边
int opt;
void add(int u, int v){
edges[cot].v = v;
edges[cot].next = head[u];
head[u] = cot++;
}
void init(int n){
cot = cnt = opt = 0;
for(int i = 0; i < n; i++){
dfn[i] = low[i] = 0;
head[i] = -1;
}
}
void tarjan(int now, int fa){
dfn[now] = low[now] = ++cnt;
for(int i = head[now]; i != -1; i = edges[i].next){
int v = edges[i].v;
if(!dfn[v]){
tarjan(v, now);
low[now] = min(low[now], low[v]);
if(low[v] > dfn[now]){ //割边判定条件
p[opt].first = now;
p[opt].second = v;
if(now > v)
swap(p[opt].first, p[opt].second);
opt++;
}
}
else if(v != fa) low[now] = min(low[now], dfn[v]); //排除返祖边的影响
}
}
int main(){
int n;
int sum, num, point;
char a, b;
while(~scanf("%d", &n)){
init(n);
for(int i = 0; i < n; i++){
scanf("%d (%d)", &sum, &num);
for(int j = 0; j < num; j++){
scanf("%d", &point);
add(sum, point);
}
}
for(int i = 0; i < n; i++) if(!dfn[i]) tarjan(i, -1);
sort(p, p + opt);
printf("%d critical links\n", opt);
for(int i = 0; i < opt; i++){
printf("%d - %d\n", p[i].first, p[i].second);
}
puts(""); //被这个狗换行卡了,以后注意输出格式的惨痛教训
}
}