当我在准备做基环树的题时,经常有了正解的思路确发现不会找环,,,,,,因为我实在太蒻了。

所以我准备梳理一下找环的方法:

有向图

先维护一个栈,把遍历到的节点一个个地入栈。当我们从一个节点x回溯时无非两种情况:

1.从x延伸出去的环已经被找完;

2.从x延伸出去的地方并没有环;

也就是说从x延伸出去的地方包括x都已经对我们现在毫无意义了。所以说,当一个点回溯时,把它出栈。

当下一步要到的点在栈中,那说明找到了环。此时把栈中的节点拎出来打上标记即可。

#include<iostream>
#include<cstring>
#include<cstdio>
#define maxn 500001
using namespace std; struct edge{
int to,next;
edge(){}
edge(const int &_to,const int &_next){ to=_to,next=_next; }
}e[maxn];
int head[maxn],k; int stack[maxn],top;
bool vis[maxn],instack[maxn],inloop[maxn];
int n,m; inline int read(){
register int x(0),f(1); register char c(getchar());
while(c<'0'||'9'<c){ if(c=='-') f=-1; c=getchar(); }
while('0'<=c&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
inline void add(const int &u,const int &v){
e[k]=edge(v,head[u]);
head[u]=k++;
} void dfs(int u){
vis[u]=instack[u]=true,stack[++top]=u;
for(register int i=head[u];~i;i=e[i].next){
int v=e[i].to;
if(!vis[v]) dfs(v);
else if(instack[v]){
int w,t=top;
do{
w=stack[t--],instack[w]=false,inloop[w]=true;
}while(w!=v);
}
}
instack[u]=false,top--;
} int main(){
memset(head,-1,sizeof head);
n=read(),m=read();
for(register int i=1;i<=m;i++){
int u=read(),v=read();
add(u,v);
}
for(register int i=1;i<=n;i++) if(!vis[i]) dfs(i);
for(register int i=1;i<=n;i++) printf("%d:%d\n",i,inloop[i]);
return 0;
}

感性地理解一下还是能懂的?

无向图

如果你用上面的方法来跑无向图......你会发现红得好凄惨

原因是:把无向边也当成了环。

所以我们得换个方法。可以先跑个搜索树出来,并记录fa数组表示图中节点在搜索树上的父亲节点,并记录下搜索的时间戳dfn。当下个节点的dfn为0时,往下走;否则找到了环。

如果你真的像上面那样做了,你会发现同一个环被记录了两次。如果你想优化常数,你当然不愿意这种事发生;或者你想把环上节点输出而不只是打个标记而已,你就更不能让它发生了。这时我们记录的dfn才是真正发挥作用的时候。我们可以判断一下当前点和下个点的时间戳的大小关系。为了能够输出环上节点,我们才记录了fa数组,这时它就发挥作用了:可以通过不断遍历fa数组把环输出。但关键就是大小关系是什么?随便写一个吗?肯定不是。可以知道dfn[fa[x]]<dfn[x],所以我们应该从环上dfn值最大的点开始往回走。所以说,用来判断的大小关系为:if(当前节点的dfn值小于下一个点) then 从下一个点开始遍历fa数组。

#include<iostream>
#include<cstring>
#include<cstdio>
#define maxn 500001
using namespace std; struct edge{
int to,next;
edge(){}
edge(const int &_to,const int &_next){ to=_to,next=_next; }
}e[maxn<<1];
int head[maxn],k; int dfn[maxn],fa[maxn],tot;
int n,m; inline int read(){
register int x(0),f(1); register char c(getchar());
while(c<'0'||'9'<c){ if(c=='-') f=-1; c=getchar(); }
while('0'<=c&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
inline void add(const int &u,const int &v){
e[k]=edge(v,head[u]);
head[u]=k++;
} void dfs(int u){
dfn[u]=++tot;
for(register int i=head[u];~i;i=e[i].next){
int v=e[i].to;
if(v==fa[u]) continue;
if(!dfn[v]) fa[v]=u,dfs(v);
else if(dfn[u]<dfn[v]){
printf("%d",v);
do{
printf(" %d",fa[v]),v=fa[v];
}while(v!=u);
puts("");
}
}
} int main(){
memset(head,-1,sizeof head);
n=read(),m=read();
for(register int i=1;i<=m;i++){
int u=read(),v=read();
add(u,v),add(v,u);
}
for(register int i=1;i<=n;i++) if(!dfn[i]) dfs(i);
return 0;
}

不过无向图的算法就找不出一元环了。但哪题用到了一元环呢?


两个算法的复杂度都是O(N)。

与图论的邂逅06:dfs找环的更多相关文章

  1. # 「银联初赛第一场」自学图论的码队弟弟(dfs找环+巧解n个二元一次方程)

    「银联初赛第一场」自学图论的码队弟弟(dfs找环+巧解n个二元一次方程) 题链 题意:n条边n个节点的连通图,边权为两个节点的权值之和,没有「自环」或「重边」,给出的图中有且只有一个包括奇数个结点的环 ...

  2. Codeforces Round #369 (Div. 2) D. Directed Roads —— DFS找环 + 快速幂

    题目链接:http://codeforces.com/problemset/problem/711/D D. Directed Roads time limit per test 2 seconds ...

  3. CodeForces - 103B(思维+dfs找环)

    题意 https://vjudge.net/problem/CodeForces-103B 很久很久以前的一天,一位美男子来到海边,海上狂风大作.美男子希望在海中找到美人鱼 ,但是很不幸他只找到了章鱼 ...

  4. CodeForces 711D Directed Roads (DFS找环+组合数)

    <题目链接> 题目大意: 给定一个$n$条边,$n$个点的图,每个点只有一条出边(初始状态),现在能够任意对图上的边进行翻转,问你能够使得该有向图不出先环的方案数有多少种. 解题分析: 很 ...

  5. HDU - 6370 Werewolf 2018 Multi-University Training Contest 6 (DFS找环)

    求确定身份的人的个数. 只能确定狼的身份,因为只能找到谁说了谎.但一个人是否是民,无法确定. 将人视作点,指认关系视作边,有狼边和民边两种边. 确定狼的方法只有两种: 1. 在一个仅由一条狼边组成的环 ...

  6. UVaLive 6950 && Gym 100299K Digraphs (DFS找环或者是找最长链)

    题意:有n个只包含两个字母的字符串, 要求构造一个m*m的字母矩阵, 使得矩阵的每行每列都不包含所给的字符串, m要尽量大, 如果大于20的话构造20*20的矩阵就行了. 析:开始吧,并没有读对题意, ...

  7. [NOI2008]假面舞会——数论+dfs找环

    原题戳这里 思路 分三种情况讨论: 1.有环 那显然是对于环长取个\(gcd\) 2.有类环 也就是这种情况 1→2→3→4→5→6→7,1→8→9→7 假设第一条链的长度为\(l_1\),第二条为\ ...

  8. [蓝桥杯2018初赛]小朋友崇拜圈(dfs找环)

    传送门 思路: 题意大意:n条有向边,找出最大环. 我们发现,如果一个小朋友没有被任何人崇拜,那么他一定不位于环中.为此我们可以设置一个indug数组预处理.如果2被崇拜了那么indug[2]就加加, ...

  9. New Reform---cf659E(dfs找环)

    题目链接:http://codeforces.com/problemset/problem/659/E 给你n个点,m条双向边,然后让你把这些边变成有向边,使得最后的图中入度为0的点的个数最少,求最少 ...

随机推荐

  1. OS第六章

    OS第七次实验 多进程 添加一个进程体 添加进程B,首先设置i的初值为0x1000,这样来方便程序运行时的时候能区分.其余地方与A一致. 相关变量和宏 Minix中定义了一个数组,叫做tasktab的 ...

  2. 仵航说 Vue项目如何打包并发布到linux服务器 仵老大

    1,打包 1.1首先你会在本地编辑好你的代码, 1.2然后在控制台输入 npm run build npm run build 1.3稍等片刻就打包完毕 2,位置 2.1打包完毕之后会在项目中生成一个 ...

  3. C# AutoMapper:流行的对象映射框架,可减少大量硬编码,很小巧灵活,性能表现也可接受。

    AutoMapper 是一个对象-对象映射器,可以将一个对象映射到另一个对象. 官网地址:http://automapper.org/ 官方文档:https://docs.automapper.org ...

  4. React Hook 入门使用

    React Hook 是什么 1.没有比官网说的更好的 HOOK 1. React Hook 官方 2. 用我们自己的话说,它是一个钩子函数,用来处理组件间的状态的一个方法,暂时理解为一个高阶函数吧. ...

  5. 将从数据库查询出来的带有父子结构的list转换成treeList结构

    package test; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedSta ...

  6. [leetcode349]Intersection of Two Arrays

    设计的很烂的一道题 List<Integer> res = new ArrayList<>(); // int l1 = nums1.length; // int l2 = n ...

  7. JS验证三种提示框

    1 <form> 2 <!-- 按钮选择点击 --> 3 <tr> 4 <td><input type="button" na ...

  8. AngularJS 遗留项目的升级改造之路(一)

    目录 序言 遗留项目概述 条件限制下的升级原则 升级改造的演进方向 遇到的主要难点 小结 参考 1. 序言 Angular 官方网站针对 从 AngularJS 升级到 Angular 提供了比较详细 ...

  9. Java中jna的用法

    (1)jna是对jni的封装,让java使用者能更好的使用本地的动态库 (2)使用jna需要下载jna的jar包,该jar包就是对jni的封装,所以在调用效率上来讲,jna是要比jni低一点的,不过对 ...

  10. 再看C语言-算法

    通常一个程序包括算法.数据结构.程序设计方法及语言工具和环境这四个方面.其中算法是核心,算法就是解决"做什么"和"如何做"的问题.算法是程序的灵魂,项目中如果接 ...