zoukankan      html  css  js  c++  java
  • 强连通分量--tarjan算法

    今天学了一个强连通分量,用tarjan做。北京之前讲过,今天讲完和之前一样,没有什么进步。上课没听讲,只好回来搞,这里安利一个博客:链接

    https://blog.csdn.net/qq_34374664/article/details/77488976

    讲一下我自己的体会吧,其实就是维护一个栈,然后树上跑dfs,每个节点存两个值:dn和low,dn代表dfs的顺序(时间),low代表的是他可以连通的最小的节点。

    模拟一下,然后就会发现,其实整个算法就是模拟了一下将每个点压入栈。然后遇到之前在栈里的元素,向后弹出到这一位就行了。

    洛谷板子题:链接

    直接上代码,很好懂。

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int m,n,len = 0,stc[50050];
    int ri = 0,tot = 0,ans = 0;
    int num[50005];
    struct node{
        int l,r,nxt;
    }a[50050];
    int low[50050],lst[50050];
    int dn[50080];
    int chu[50050];
    bool vis[50050];
    int add(int x,int y)
    {
        a[++len].l = x;
        a[len].r = y;
        a[len].nxt = lst[x];
        lst[x] = len;
        chu[x]++;
    }
    void dfs(int x)
    {
        dn[x] = low[x] = ++tot;
        stc[++ri] = x;
        vis[x] = 1;
        for(int k = lst[x];k;k = a[k].nxt)
        {
            int y = a[k].r;
            if(!dn[y])
            {
                dfs(y);
                low[x] = min(low[x],low[y]);
            }
            else if(vis[y])
            {
                low[x] = min(low[x],dn[y]);
            }
        }
        if(low[x] == dn[x])
        {
            ans ++;
            int v;
            do
            {
                num[ans]++;
                vis[stc[ri]] = 0;
                v = stc[ri--];
            }
            while(x != v);
        }
    }
    int main()
    {
        memset(vis,0,sizeof(vis));
        scanf("%d%d",&n,&m);
        int x,y;
        for(int i = 1;i <= m;i++)
        {
            scanf("%d%d",&x,&y);
            add(x,y);
        }
        for(int i = 1;i <= n;i++)
            if(dn[i] == 0)
            {
                dfs(i);
            }
    //    cout<<ans<<endl;
        tot = 0;
        for(int i = 1;i <= ans;i++)
        {
            if(num[i] > 1)
            {
                tot++;
            }
        }
        printf("%d
    ",tot);
        return 0;
    }

     洛谷日推了一道题,也是板子,写一下:

    每头奶牛都梦想成为牛棚里的明星。被所有奶牛喜欢的奶牛就是一头明星奶牛。所有奶
    
    牛都是自恋狂,每头奶牛总是喜欢自己的。奶牛之间的“喜欢”是可以传递的——如果A喜
    
    欢B,B喜欢C,那么A也喜欢C。牛栏里共有N 头奶牛,给定一些奶牛之间的爱慕关系,请你
    
    算出有多少头奶牛可以当明星。
    输入输出格式
    输入格式:
    
     第一行:两个用空格分开的整数:N和M
    
     第二行到第M + 1行:每行两个用空格分开的整数:A和B,表示A喜欢B
    
    输出格式:
    
     第一行:单独一个整数,表示明星奶牛的数量
    
    输入输出样例
    输入样例#1: 复制
    
    3 3
    1 2
    2 1
    2 3
    
    输出样例#1: 复制
    
    1

    直接上代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    template <class T>
    void read(T &x)
    {
        char c;
        bool op = 0;
        while(c = getchar(),c < '0' || c > '9')
            if(c == '-' ) op = 1;
        x = c - '0';
        while(c = getchar(),c >= '0' && c <= '9')
            x =x * 10 + c - '0';
        if(op)
        x = -x;
    }
    int lst[50010],dfn[50010],low[50010],n,m,tot = 0,str[50010],top = 0,vis[50010];
    int num[50010],chu[50010],col[50010],len,ans;
    struct node{
        int l,r,nxt;
    }a[50010];
    void add(int x,int y)
    {
        a[++len].l = x;
        a[len].r = y;
        a[len].nxt = lst[x];
        lst[x] =len;
    //    chu[x]++;
    }
    void tarjan(int x)
    {
        dfn[x] = low[x] = ++tot;
        str[++top] = x;
        vis[x] = 1;
        for(int k = lst[x];k;k = a[k].nxt)
        {
            int y = a[k].r;
            if(!dfn[y])
            {
                tarjan(y);
                low[x] = min(low[x],low[y]);
            }
            else if(vis[y])
            {
                low[x] = min(low[x],dfn[y]);
            }
        }
        if(low[x] == dfn[x])
        {
            ans++;
            int v;
            do
            {
                num[ans]++;
                vis[str[top]] = 0;
                v = str[top--];
                col[v] = ans;
            }
            while(x != v);
        }
    }
    int main()
    {
        read(n);read(m);
        for(int i = 1;i <= m;i++)
        {
            int x,y;
            read(x);read(y);
            add(x,y);
        }
        for(int i = 1;i <= n;i++)
        {
            if(dfn[i] == 0)
            {
                tarjan(i);
            }
        }
        for(int i = 1;i <= n;i++)
        {
            for(int k = lst[i];k;k = a[k].nxt)
            {
                if(col[a[k].l] != col[a[k].r])
                {
                    chu[col[a[k].l]]++;
                }
            }
        }
        int tot= 0,f;
        for(int i = 1;i <= ans;i++)
        {
            if(chu[i] == 0)
            {
                tot++;
                f = i;
            }
            if(tot >= 2)
            {
                puts("0");
                return 0;
            }
        }
        printf("%d
    ",num[f]);
        return 0;
    }
  • 相关阅读:
    Window_Open详解
    ajax josn 城市
    图片在容器里垂直居中
    Asp.Net 分段,断点,下载
    HTTP Range
    看得懂的看
    反射访问非公共成员
    C# Rijndael 大文件 分割/合并 并 加密
    在存储过程中如何实现将id列表字符串传入in()
    《植物大战僵尸》网页脚本版
  • 原文地址:https://www.cnblogs.com/DukeLv/p/9383780.html
Copyright © 2011-2022 走看看