与图论的邂逅06:dfs找环

当我在准备做基环树的题时,经常有了正解的思路确发现不会找环,,,,,,因为我实在太蒻了。
所以我准备梳理一下找环的方法:

有向图

先维护一个栈,把遍历到的节点一个个地入栈。当我们从一个节点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)。

转载于:https://www.cnblogs.com/akura/p/10838613.html

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值