「算法学习」圆方树——处理仙人掌的利器

圆方树大概分两种,一个是圆方树,一个是广义圆方树。

圆方树

这可以解决仙人掌上的问题。

任意一条边至多只出现在一条简单回路的无向连通图称为仙人掌。

很多题解将其作为无向图构建,本文将其构建为外向树,在这个问题中两种构建方式不会影响求解。

构建方式

记读入的图为原图,构建的圆方树为新图。

首先,新图保留着原图的点集,这些点记为圆点。

将原图任意一个点(实现中指定  1 号点即可)作为根节点,然后在原图跑一遍 dfs。

每当找到一个环的时候(用 tarjan 算法),将进入环的点(也就是边双的根节点)记为头节点,然后在新图上对加一个方点,并让头节点向这个方点连边,同时,方点向其它点  连边。

下面使原图与对应新图(圆方树)的一个例子:

tarjan 算法变形

如何才能找到所有的简单环?为了方便,用边双连通分量求解。但是边双是不能轻易处理简单环的,比如这个图:

 

1,2,3,4,5,6,就是一个边双,但显然不是一个简单环。但是点双可以处理,至于为什么不用点双,可能是因为两者都可,而边双看起来简洁。如何改进边双:

原算法中,只要还在栈中,我们都将其看为一个边双联通分量。

但是,现在如果我们还遇到这种情况的话,就应该一个一个的处理为一个一个的简单环,而不是揉在一起。

 

比如这张图,黑色边组成一颗搜索树,当  去搜儿子  号点时,发现  号点被搜过,那么就可以从  号点开始倒推搜索树,一直到 ,这样就可以找出简单环。

all in all,看一下代码:

void build_circle(int x, int y, int z) {
	for (int i = y; i != x; i = fat[i]) {
		...
	}
	...
}

void tarjan(int u, int from) {
	dfn[u] = low[u] = ++idx;
	for (int p = first[u]; p; p = ed[p].next) {
		int v = ed[p].e, w = ed[p].d;
		if (!dfn[v]) {
			fat[v] = u, fwt[v] = w, fet[v] = p;
			tarjan(v, p);
			low[u] = min(low[u], low[v]);
			if (dfn[u] < low[v]) G[u].push_back({v, w});
		}
		else if (p != (from ^ 1)) low[u] = min(low[u], dfn[v]);
	}
	for (int p = first[u]; p; p = ed[p].next) {
		int v = ed[p].e, w = ed[p].d;
		if (dfn[u] < dfn[v] && fet[v] != p) build_circle(u, v, w); // 开始倒退成环
	}
}

今天就到这里了。 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值