• [强联通分量_tarjan] 0725


    tarjan算法的思路不难理解,用low来标记同一个强联通分量中的点,初始时low[i]=dfn[i],当访问到已经在当前栈中的顶点时,相当于找到了一个强联通分量的根节点(一个强联通分量中最早访问到的作为根节点),然后递归地更新栈中所有节点,如果遇到low[k]=dfn[k],说明k就是这个分量的根,于是对之前的所有点出栈并标记(此处标记用连续的值1,2,3代表第几个强联通分量,其实可以直接用low就行,cols记录强联通分量的个数)。

      1 # include <cstdio>
      2 # include <cstring>
      3 
      4 # define N (100 + 5)
      5 # define M (N * N)
      6 
      7 int n, m;
      8 int top, cols, tmpdfn;
      9 char ins[N];
     10 int first[N], dfn[N], low[N], s[N], in[N], out[N], c[N];
     11 int u[M], v[M], next[M];
     12 
     13 int Min(int x, int y)
     14 {
     15     return x<y ? x:y;
     16 }
     17 
     18 void tarjan(int x)
     19 {
     20     dfn[x] = low[x] = ++tmpdfn;
     21     s[top++] = x;
     22     ins[x] = 1;
     23     for (int e = first[x]; e != -1; e = next[e])
     24     {
     25         int y = v[e];
     26         if (!dfn[y])
     27         {
     28             tarjan(y);
     29             low[x] = Min(low[x], low[y]);
     30         }
     31         else if (ins[y])
     32             low[x] = Min(low[x], dfn[y]);
     33     }
     34     if (dfn[x] == low[x])
     35     {
     36         int t;
     37         ++cols;
     38         do
     39         {
     40             t = s[--top];
     41             ins[t] = 0;
     42             c[t] = cols;
     43         } while (x != t);
     44     }
     45 }
     46 
     47 void cal(void)
     48 {
     49     top = 0, cols = 0, tmpdfn = 0;
     50     memset(dfn+1, 0, sizeof(int)*n);
     51     memset(ins+1, 0, sizeof(char)*n);
     52     for (int i = 1; i <= n; ++i) if (!dfn[i])
     53         tarjan(i);
     54 }
     55 
     56 void solve(void)
     57 {
     58     cal();
     59     if (cols == 1)
     60     {
     61         printf("1\n0\n");
     62         return ;
     63     }
     64     memset(in+1, 0, sizeof(int)*cols);
     65     memset(out+1, 0, sizeof(int)*cols);
     66     for (int i = 1; i <= n; ++i)
     67     for (int e = first[i]; e != -1; e = next[e])
     68     {
     69         int j = v[e];
     70         if (c[i] != c[j])
     71         {
     72             ++in[c[j]];
     73             ++out[c[i]];
     74         }
     75     }
     76     int zin = 0, zout = 0;
     77     for (int i = 1; i <= cols; ++i)
     78     {
     79         if (!in[i]) ++zin;
     80         if (!out[i]) ++zout;
     81     }
     82     printf("%d\n%d\n", zin, zin>zout ? zin:zout);
     83 }
     84 
     85 void read_graph(void)
     86 {
     87     memset(first+1, -1, sizeof(int)*n);
     88     for (int i = 1; i <= m; ++i)
     89     {
     90         scanf("%d%d", &u[i], &v[i]);
     91         next[i] = first[u[i]];
     92         first[u[i]] = i;
     93     }
     94 }
     95 
     96 int main()
     97 {
     98     while (~scanf("%d%d", &n, &m))
     99     {
    100         read_graph();
    101         solve();
    102     }
    103     
    104     return 0;
    105 }

    targin的主体比较”死板“,整体相比两遍DFS(kosaraju)代码不短多少,只是思路是一条线,复杂度和后者只是常数的区别,但可以使用邻接表加速,后者不好使用。

  • 相关阅读:
    序列信号发生器
    C++ | 虚函数初探
    Linux编程 | 使用 make
    [] CSDN-markdown编辑器语法——字体、字号与颜色
    Linux基础学习 | 用户及用户组
    Linux文件管理 | Liunx 常用命令
    Linux文件管理 | Linux 文件基础知识
    asp.net 可视化操作(二)——Sql数据库连接及简单查询功能的实现
    asp.net 可视化操作(一)——asp.net安装与使用
    Linux基础学习 | gcc、g++的安装和使用
  • 原文地址:https://www.cnblogs.com/JMDWQ/p/2626243.html
Copyright © 2020-2023  润新知