tarjan求割点和割边

概念

割点 :在一个无向图中,如果删除某个顶点,这个图就不再连通(任意两点之间无法相互到达),那么这个顶点就是这个图的割点。

割边(桥):在一个无向图中删除某条边后,图不再连通,那么这条边就是这个图的割边(也叫作桥)

求法

  • 割点: 一个顶点\(x\)是割点,当且仅当满足:
  1. \(x\)为树根,且\(x\)有多于一个子树。

  2. \(x\)不为树根,且满足\(x\)\(to\)在搜索树中的父亲,并使得\(low_{to}\le dfn_x\).(因为删去\(x\)\(to\)以及\(to\)的子树不能到达\(x\)的其他子树以及祖先)

code:

#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
int read(){
	int x = 1,a = 0;char ch = getchar();
	while (ch < '0'||ch > '9'){if (ch == '-') x = -1;ch = getchar();}
	while (ch >= '0'&&ch <= '9'){a = a*10+ch-'0';ch = getchar();}
	return x*a; 
}
const int maxn = 1e5+10;
int n,m;
struct node{
	int to,next;
}ed[maxn*2];
int head[maxn*2],tot;
void add(int u,int to){
	ed[++tot].to = to;
	ed[tot].next = head[u];
	head[u] = tot;
}
int dfn[maxn],low[maxn],flag[maxn],cnt,child;
void tarjan(int x,int fa){
	dfn[x] = low[x] = ++cnt;
	for (int i = head[x];i;i = ed[i].next){
		int to = ed[i].to;
		if (!dfn[to]){
			tarjan(to,fa);
			low[x] = min(low[x],low[to]);
			if (low[to] >= dfn[x]&&x != fa) flag[x] = 1;	
			if (x == fa) child++;
		}
		low[x] = min(low[x],dfn[to]);
	}
	if (child >= 2&&x == fa) flag[x] = 1;
}
int main(){
	n = read(),m = read();
	for (int i = 1;i <= m;i++){
		int x = read(),y = read();
		add(x,y),add(y,x);
	}
	for (int i = 1;i <= n;i++){
		if (!dfn[i]) child = 0,tarjan(i,i);
	}
	int tot = 0;
	for (int i = 1;i <= n;i++){
		if (flag[i]) tot++;
	}
	printf("%d\n",tot);
	for (int i = 1;i <= n;i++){
		if (flag[i]) printf("%d ",i);
	}
	return 0;
} 
  • 割边:
  1. 一条无向边\((x,to)\)是桥,满足\(low_{to}>dfn_x\).(因为\(to\)想要到达\(x\)的父亲必须经过\((x,to)\)这条边,所以删去这条边图不连通)

  2. 实现时因为是无向图,建反边两条边都要标记上,边从\(1\)开始编号,正向边\(x\)的反向边就是\(x\)^\(1\)

code:

#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
int read(){
	int x = 1,a = 0;char ch = getchar();
	while (ch < '0'||ch > '9'){if (ch == '-') x = -1;ch = getchar();}
	while (ch >= '0'&&ch <= '9'){a = a*10+ch-'0';ch = getchar();}
	return x*a;
}
const int maxn = 3e6+10;
int n,m;
struct node{
	int to,next;
}ed[maxn*2];
int head[maxn*2],tot = 1;
void add(int u,int to){
	ed[++tot].to = to;
	ed[tot].next = head[u];
	head[u] = tot;
}
int dfn[maxn],low[maxn],cnt,res;
int bridge[maxn];
void tarjan(int x, int fa) {
    dfn[x] = low[x] = ++cnt;
    for (int i = head[x];i;i = ed[i].next) {
        int to = ed[i].to;
        if (!dfn[to]) {
            tarjan(to, i);
            low[x] = min(low[x],low[to]);
            if (low[to] > dfn[x])
                bridge[i] = bridge[i^1] = true;
        }
        else if (i != (fa^1)) low[x] = min(low[x],dfn[to]);
    }
}
int main(){
	n = read(),m = read();
	for (int i = 1;i <= m;i++){
		int u = read(),v = read();
		add(u,v),add(v,u);
	}
	for (int i = 1;i <= n;i++){
		if (!dfn[i]) tarjan(i,0);
	}
	int res = 0;
    for (int i = 2; i < tot; i+=2)
        if (bridge[i]) res++;
	printf("%d\n",res);
	return 0;
}
posted @ 2020-11-10 16:21  小又又yyyy  阅读(193)  评论(0编辑  收藏  举报