zoukankan      html  css  js  c++  java
  • [Graph]Tarjan

    Tarjan 算法——缩点

    Tarjan算法是一种用来求解强连通分量的线性时间算法。

    先介绍几个概念:

    强连通:若两个点能相互到达,那么我们称这两个点强连通。

    强连通图:若一个图中任意两个点能相互到达,那么这个图就叫强连通图。

    强连通分量:若一个图中的某个子图中,在这个子图内的任意两点都能相互到达,那么我们称这个子图为改图的强连通分量。

    DFN数组:存储每个点的DFS序。

    LOW:每个点或它的子树能够到达的  最小的DFN(即最先进栈的点)。

     VIS:标记某个点是否在栈中

    Tarjan算法是一种基于深度优先搜索的算法,

    在搜索时边处理每个节点的DFS序边入栈,

    在回溯时处理点的low与dfn的关系,

    若相等,则从它到栈顶的元素均在同一个强连通分量中,

    (刚开始蒟蒻想了很久啊,因为是深度优先遍历啊,所以强连通分量肯定在栈中是连续的,那么在找到这个点之前那些跟此点不在同个强连通分量中肯定被弹出去了)

    均弹出栈,用一个数组记录下来。

    先把这个图当作一棵树进行深度优先遍历,

    得到每个点的DFN,

    并且在这过程中进栈,

    维护LOW值。

    如何求LOW?

    对于当前u点到v点,

    若v点之前没有被访问过,

    那么tarjan这个点,

    在回溯时若v点的low值小于u的low值,

    那么更新u的low值。

    若v被访问过并且v点的dfs序小于u的low值,

    那么更新u的low值。

    找环时在最后所有点均被访问后回溯时找出。

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<queue>
    #define maxn 100005
    #define dian 10005
    using namespace std;
    int vis[dian],book[dian],x[maxn],y[maxn],a[maxn],head[maxn],tot,cnt,low[maxn],color,top,ru[maxn],ans=-100000,dfn[maxn],sta[maxn],b[maxn],dis[maxn];
    struct Edge{
        int to,next;
    }e[maxn];
    void addedge(int a,int b){
        tot++;
        e[tot].to=b;
        e[tot].next=head[a];
        head[a]=tot; 
        ru[b]++;
    }
    void tarjan(int u){
        cnt++;
        dfn[u]=low[u]=cnt;
        top++;
        sta[top]=u;
        book[u]=1;
        for(int i=head[u];i;i=e[i].next){
            int v=e[i].to;
            if(!dfn[v]){
                tarjan(v);
                low[u]=min(low[u],low[v]);
            }
            else if(book[v]) low[u]=min(low[u],dfn[v]);
        }
        if(low[u]==dfn[u]){
            color++;
            do{
                
                vis[sta[top]]=color;
                b[color]+=a[sta[top]];
                book[sta[top]]=0;
        //        printf("%d ",top); 
            //        printf("%d ",sta[top]);
            //    top--;
            }while(sta[top--]!=u);
        //    printf("
    ");
        }
    }
    void spfa(int u){
        queue<int> q;
        memset(book,0,sizeof(book));
        memset(dis,0,sizeof(dis));
        int h=1,t=0;
        q.push(u);
        book[u]=1;
        dis[u]=b[u];
    //    printf("%d ",b[u]);
        while(!q.empty()){
            int x=q.front();
            q.pop();
            book[x]=0;
            for(int i=head[x];i;i=e[i].next){
                int v=e[i].to;
                if(dis[v]<dis[x]+b[v]){
                    dis[v]=dis[x]+b[v];
                    if(!book[v]){
                        q.push(v);
                        book[v]=1;
                    }
                }
            }
            
        }
        for(int i=1;i<=color;i++){
            ans=max(ans,dis[i]);
        }
    } 
    int main(){
        int n,m,i;
        scanf("%d%d",&n,&m);
        for(i=1;i<=n;i++)
        scanf("%d",&a[i]);
        for(i=1;i<=m;i++){
            scanf("%d%d",&x[i],&y[i]);
            addedge(x[i],y[i]);
        }
        for(i=1;i<=n;i++){
            if(!dfn[i])
            tarjan(i);
        }
        memset(e,0,sizeof(e));
        memset(head,0,sizeof(head));
        memset(ru,0,sizeof(ru)); 
        tot=0;
        for(i=1;i<=m;i++){
            if(vis[x[i]]!=vis[y[i]]){
                addedge(vis[x[i]],vis[y[i]]);
            }
        }
    //    printf("%d",color);
        for(i=1;i<=color;i++){
            if(!ru[i])
            spfa(i);
        }
        printf("%d",ans);
        return 0;
    } 
    tarjan
  • 相关阅读:
    Combine 框架,从0到1 —— 4.在 Combine 中使用计时器
    Combine 框架,从0到1 —— 4.在 Combine 中使用通知
    Combine 框架,从0到1 —— 3.使用 Subscriber 控制发布速度
    Combine 框架,从0到1 —— 2.通过 ConnectablePublisher 控制何时发布
    使用 Swift Package Manager 集成依赖库
    iOS 高效灵活地配置可复用视图组件的主题
    构建个人博客网站(基于Python Flask)
    Swift dynamic关键字
    Swift @objcMembers
    仅用递归函数操作逆序一个栈(Swift 4)
  • 原文地址:https://www.cnblogs.com/Fish-/p/8384653.html
Copyright © 2011-2022 走看看