• (笔记整理未完成)【图论】强连通分量——Tarjan算法


    知识点



    模板题:洛谷 B3609 [图论与代数结构 701] 强连通分量

    题目描述

    给定一张 n 个点 m 条边的有向图,求出其所有的强连通分量。

    注意,本题可能存在重边和自环。

    输入格式

    第一行两个正整数 n , m ,表示图的点数和边数。

    接下来 m 行,每行两个正整数 u 和 v 表示一条边。

    输出格式

    第一行一个整数表示这张图的强连通分量数目。

    接下来每行输出一个强连通分量。第一行输出 1 号点所在强连通分量,第二行输出 2 号点所在强连通分量,若已被输出,则改为输出 3 号点所在强连通分量,以此类推。每个强连通分量按节点编号大小输出。

    输入输出样例

    输入 #1

    6 8
    1 2
    1 5
    2 6
    5 6
    6 1
    5 3
    6 4
    3 4
    

    输出 #1

    3
    1 2 5 6
    3
    4
    

    说明/提示

    对于所有数据,1 \leqslant n \leqslant 100001 \leqslant m \leqslant 100000


    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. using namespace std;
    7. int n,m;
    8. const int maxn=1e6+5;
    9. struct node
    10. {
    11. int to,next;
    12. }edge[maxn<<1];
    13. int head[maxn],num=0,cnt=0,ans=0,top=0;
    14. int dfn[maxn],low[maxn],stac[maxn],color[maxn],size[maxn];
    15. bool vis[maxn];
    16. inline int read()
    17. {
    18. int x=0,f=1;
    19. char c=getchar();
    20. while(c<'0'||c>'9')
    21. {
    22. if(c=='-') f=-1;
    23. c=getchar();
    24. }
    25. while(c>='0' && c<='9')
    26. {
    27. x=(x<<3)+(x<<1)+(c^48);
    28. c=getchar();
    29. }
    30. return x*f;
    31. }
    32. inline void add(int u,int v)
    33. {
    34. edge[++num].to=v;
    35. edge[num].next=head[u];
    36. head[u]=num;
    37. }
    38. inline void tarjan(int u)
    39. {
    40. dfn[u]=low[u]=++cnt;
    41. vis[u]=1;
    42. stac[++top]=u;
    43. for(int i=head[u];i!=-1;i=edge[i].next)
    44. {
    45. int v=edge[i].to;
    46. if(!dfn[v])
    47. {
    48. tarjan(v);
    49. low[u]=min(low[u],low[v]);
    50. }
    51. else if(vis[v])
    52. {
    53. low[u]=min(low[u],dfn[v]);
    54. }
    55. }
    56. if(dfn[u] == low[u])
    57. {
    58. ans++;
    59. color[u]=ans;
    60. while(stac[top]!=u)
    61. {
    62. vis[stac[top]]=0;
    63. color[stac[top]]=ans;
    64. --top;
    65. }
    66. vis[u]=0;
    67. --top;
    68. }
    69. }
    70. int main()
    71. {
    72. n=read(); m=read();
    73. memset(head,-1,sizeof(head));
    74. for(int i=1;i<=m;++i)
    75. {
    76. int u=read(),v=read();
    77. add(u,v);
    78. }
    79. for(int i=1;i<=n;++i)
    80. {
    81. if(!dfn[i]) tarjan(i);
    82. }
    83. bool pr[maxn];
    84. printf("%d\n",ans);
    85. for(int i=1;i<=n;++i) //这道题的难点就在于最后的输出
    86. {
    87. if(pr[i] == 1) continue; //用pr[]标记是否已经输出过
    88. printf("%d ",i); //没有输出过就输出
    89. pr[i]=1; //标记,防止重复查找
    90. for(int j=i+1;j<=n;++j)
    91. {
    92. if(color[j] == color[i]) //节点j与节点i在同一个强连通分量中
    93. {
    94. printf("%d ",j);
    95. pr[j]=1;
    96. }
    97. }
    98. printf("\n");
    99. }
    100. return 0;
    101. }

    相关练习

    1 . 洛谷 P2863 [USACO06JAN]The Cow Prom S 

    题目描述

    有一个 n 个点,m 条边的有向图,请求出这个图点数大于 1 的强联通分量个数。

    输入格式

    第一行为两个整数 n 和 m。

    第二行至 m+1 行,每一行有两个整数 a 和 b,表示有一条从 a 到 b 的有向边。

    输出格式

    仅一行,表示点数大于 1 的强联通分量个数。

    输入输出样例

    输入 #1

    5 4
    2 4
    3 5
    1 2
    4 1

    输出 #1

    1

    说明/提示

    数据规模与约定

    对于全部的测试点,保证2\leqslant n \leqslant 10^42 \leqslant m \leqslant 5\times 10^41 \leqslant a, b \leqslant n


    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. using namespace std;
    7. int n,m;
    8. const int maxn=1e6+5;
    9. struct node{
    10. int to,next;
    11. }edge[maxn<<1];
    12. int head[maxn],num=0,cnt=0,top=0,ans=0,sum=0;
    13. int dfn[maxn],low[maxn],stac[maxn],color[maxn],size[maxn];
    14. /*
    15. dfn[]用于统计节点是第几个进入dfs递归的;
    16. low[]用于统计从节点i能到达的已标记过的最远节点;
    17. stac[]手打栈;
    18. color[]用于将同一个强连通分量的节点标记为一样的数字,俗称染色;
    19. size[]用于统计在一个强连通分量中的节点数量,因为只有一个节点也可以被看作一个强连通分量,因此要初始化为1;
    20. */
    21. bool vis[maxn];
    22. inline int read()
    23. {
    24. int x=0,f=1;
    25. char c=getchar();
    26. while(c<'0'||c>'9')
    27. {
    28. if(c=='-') f=-1;
    29. c=getchar();
    30. }
    31. while(c>='0' && c<='9')
    32. {
    33. x=(x<<3)+(x<<1)+(c^48);
    34. c=getchar();
    35. }
    36. return x*f;
    37. }
    38. inline void add(int u,int v)
    39. {
    40. edge[++num].to=v;
    41. edge[num].next=head[u];
    42. head[u]=num;
    43. }
    44. inline void tarjan(int u)
    45. {
    46. dfn[u]=low[u]=++cnt; //输入进来的时候,dfn[]和low[]都标记为第cnt个进入的节点
    47. vis[u]=1;
    48. stac[++top]=u; //将节点u存入栈中
    49. for(int i=head[u];i!=-1;i=edge[i].next)
    50. {
    51. int v=edge[i].to;
    52. if(!dfn[v]) //如果与节点i相连的节点v没有被找过
    53. {
    54. tarjan(v);
    55. low[u]=min(low[u],low[v]); //low[]标记为能找到的最早的节点;
    56. }
    57. else
    58. {
    59. if(vis[v])
    60. {
    61. low[u]=min(low[u],dfn[v]); //dfn[v]也可以改为low[v];
    62. }
    63. }
    64. }
    65. if(dfn[u] == low[u]) //u为强联通分量的根,也就是能到达的最小节点
    66. {
    67. ans++; //统计第几个强连通分量的集
    68. vis[u]=0;
    69. while(stac[top]!=u)
    70. {
    71. color[stac[top]]=ans; //染色
    72. vis[stac[top]]=0; //弹出栈vis恢复
    73. top--;
    74. size[u]++; //强连通分量集内的节点个数加1;
    75. }
    76. color[stac[top]]=ans;
    77. top--;
    78. if(size[u]>1) sum++;
    79. }
    80. }
    81. int main()
    82. {
    83. n=read(); m=read();
    84. memset(head,-1,sizeof(head));
    85. for(int i=1;i<=m;++i)
    86. {
    87. int u=read(),v=read();
    88. add(u,v);
    89. }
    90. for(int i=1;i<=n;++i) //在此题中没有这一段初始化就会84pts
    91. {
    92. size[i]=1;
    93. }
    94. for(int i=1;i<=n;++i)
    95. {
    96. if(!dfn[i]) //如果该点没有被统计过
    97. tarjan(i); //继续查找
    98. }
    99. printf("%d",sum);
    100. return 0;
    101. }

  • 相关阅读:
    WebWall-03.XSS(跨站脚本漏洞)
    群晖上搭建Ghost博客
    郑泽康:一名热爱技术的“保安”
    Springboot毕设项目公共台账管理系统5d5ba(java+VUE+Mybatis+Maven+Mysql)
    不习惯的 Vue3 起步六 の Echarts绘制下钻地图
    Windows 10驱动开发入门(五):创建虚拟显示器 Indirect Display驱动开发
    Python实现视频自动打码,不用担心透露隐私了
    论文笔记 Stochastic Gradient Hamiltonian Monte Carlo (SGHMC)
    openEuler 服务器安装 JumpServer (all-in-one 模式)
    Flask入门学习教程
  • 原文地址:https://blog.csdn.net/gzkeylucky/article/details/126193946