Tarjan求割点原理 + UVA 315

割点

无向图中.删去节点x,及其所有与x关联的边后.分成2个及以上的不相连的子图.则称x是图的割点

  • 搜索树:dfs遍历无向图的访问的顺序.每个点只遍历了一次,生成的树

割点判定法则

  1. x不是搜索树的根节点,且存在yx的子节点.
  • 满足low[y]>=dfn[x]

说明从yy子树出发的点能访问到的最早的点只能达到x点(low[y]==dfn[x]),不能到达dfs访问更早的点.这说明x->y这部分存在一条边或者一个环.

  • 因为此时x不是根节点.在搜索树中x还有入边,还有比x被访问到的更早的点(说明x入边这里存在一个点或者一个联通分量).
  • 所以此时只要有一条满足low[y]>=dfn[x],x就是割点,必然能割出2个子图来
  1. x是根节点.x点在搜索树中不存在入边,那么肯定要有2个满足low[y]>=dfn[x]的子节点y才能割出2个及以上的子图

例题UVA 315

#include <iostream>
#include <string.h>
#include <algorithm>
using namespace std;
const int maxn = 1e5 + 10;

int head[maxn], ver[maxn << 1], next1[maxn << 1];
int dfn[maxn], low[maxn];
int n, m, tot, num, root;
bool cut[maxn];

void init() {
	tot = 0; num = 0; 
	memset(cut,0,sizeof cut);
	
	memset(head,0,sizeof head);
	memset(ver,0,sizeof ver);
	memset(next1,0,sizeof next1);
	
	memset(low,0,sizeof low);
	memset(dfn,0,sizeof dfn);
	
}
void edgeadd(int x,int y) { // <= tot
	ver[++tot] = y; next1[tot] = head[x]; head[x] = tot;
}
void tarjan(int x) {
	dfn[x] = low[x] = ++num;
	int out = 0;
	for (int i = head[x]; i; i = next1[i]) {
		int to = ver[i];
		if (!dfn[to]) { //to没有被访问
			tarjan(to);
			low[x] = min(low[x],low[to]); //回溯时更新由子节点更新low
			//割点存在条件: low[to]>=dfn[x] 说明:to(x的子节点)最多只能访问到x节点.
			if (low[to] >= dfn[x]) {
				out++;
				//如果是x是根节点,要有2个to(子节点)能访问到自身或者x,才能算割点
				//如果只有一个,那么根节点没有入边,删除根节点和指向to的边,仍然只有1个连通分量
				if (x != root || out > 1) { //x不是根节.那么x还有入边,入边那边还有点(连通分量).只有有一个就能构成割点.因为x
					cut[x] = 1;
				}
			}	
		}
		else //说明下个节点已经被访问.这条边构成了环
				low[x] = min(low[x],dfn[to]);
	}
}
int main() {
	int n;
//	freopen("a.txt", "r", stdin);
	while (scanf("%d", &n) && n) {
		int cur, to; 
		init();
		while (scanf("%d", &cur) && cur) {
			while (getchar() != '\n') { 
				scanf("%d", &to);
				if (cur == to) continue;
				edgeadd(cur,to); edgeadd(to,cur);
			}
		}
		for (int i = 1; i <= n; i++)
			if (!dfn[i]) root = i, tarjan(i);
		int ans = 0;
		for (int i = 1; i <= n; i++)
			if (cut[i]) ans++;
		printf("%d\n",ans);
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值