基环树找环算法
无向基环树
内向基环树 外向基环树
void dfs_1(int u)
{
dfn[u] = ++id;
for(int i=h[u];~i;i=ne[i])
{
int j = e[i];
if(j==pre[u])continue;
if(dfn[j])
{
if(dfn[j]<dfn[u])continue;
//找到闭环 u可以到达比它大的点
loop[++cnt] = j;
for(;j!=u;j=pre[j])loop[++cnt] = pre[j];
return ;
}
else {
pre[j] = u;
dfs_1(j);
}
}
}
更简单的思路,https://zhuanlan.zhihu.com/p/570142997
这个算法需要保证运行在,一个连通的内向基环树上来做
回溯法找环
- 基环树从任意一个点一定能找到环
// 找环,存在circle数组中
int find_circle(int u, int fa) {
if(vis[u]) return u;
vis[u] = 1;
for(int i = h[u]; ~i ; i = ne[i]) {
int j = e[i];
if(j == fa) continue;
int t = find_circle(j, u);
if(t) {
//vis=2 标记环上的点
circle[++ top] = u;
v[top] = w[i]; // 记录边上的权值
vis[u] = 2;
return t == u ? 0 : t;
}
}
return 0;
}
一个内向基环树的更简单找法,来自: https://usaco.org/current/data/sol_prob1_silver_jan23.html
从一个非连通的图上DAG与spanning tree的组成的图上找,环
原理:
- 从任意一个点开始向前遍历没有访问的点
a b
| |
\ |
\ |
\ |
\|
x
|
|
y
原理: 二次遍历法
after[100]; // after[i] 表示下一点是哪个点
int vis[100];
bool find_circle(int u) {
int a = u;
while( !vis[a]){
vis[a] = u;
if( (a == after[a]) == 0) return 0;
}
if( vis[a] != u) return 0;
a = after[u];
while(1){
// do somthing to a
circle[++top] = a;
if(a == u) break;
a = after[a];
}
return 1;
}
for(int i =1;i<=n;i++) {
if( !vis[i]) find_circle(i)
}
内向基环树:每个点出度都是1
找到环上的一个点,然后从环上出发就可以找到所有的点
// nxt[u] 表示下一个点
void dfs(int u) {
dfn[u] = ++id;
if( dfn[nxt[u]] ) mark = u;
else dfn[]
}
外向基环树:每个点入度都是1
反向建图,用内向基树的作法来做.