• 无向图双连通分量(DCC)


    目录

    1.边连通分量

     1.冗余路径

    2.点连通分量

     1.电力

    2.矿场搭建


    1.边连通分量

    边连通分量(e-DCC): 就是删除无向图的一条边,假如该图不连通了,则这条边叫做桥,边连通分量就是极大的不含桥的连通分量

    1.弄连个时间戳dfn表示第一次遍历到这个点的时间戳,low是他的子树中的最小的时间戳

    2.假如是桥,则dfn[x]y这条边就是桥

    3.用stack存边连通分量,因为假如low[x]==dfn[x],说明找到了这个点的祖宗节点,则入栈的就是他的子树,也就是边连通分量

     1.冗余路径

    395. 冗余路径 - AcWing题库

    题目大意就是:给定一个无向连通图,问最少加多少条边,使得该图变成一个边双连通分量

    先做一遍边的双连通分量,然后缩点建图,变成一棵树,答案就叶节点(也即度数为1的点)的一半向上取整,则ans=[cnt+1]/2

    1. #include
    2. using namespace std;
    3. typedef long long ll;
    4. const int N=5010,M=20010;
    5. int n,m;
    6. int h[N],e[M],ne[M],idx;
    7. int dfn[N],low[N],timestamp;
    8. int stk[N],top;
    9. int id[N],dcc_cnt;
    10. bool is_bridge[M];
    11. int d[N];
    12. void add(int a,int b)
    13. {
    14. e[idx]=b,ne[idx]=h[a],h[a]=idx++;
    15. }
    16. void tarjan(int u, int from)//u是当前节点,from是从那个节点更新过来的
    17. {
    18. dfn[u]=low[u]=++timestamp;//第一次遍历让她们都等于时间戳
    19. stk[++top]=u;//把这个点入栈
    20. for(int i=h[u];~i;i=ne[i])
    21. {
    22. int j=e[i];
    23. if(!dfn[j])//假如没有遍历过
    24. {
    25. tarjan(j,i);//遍历一遍
    26. low[u]=min(low[u],low[j]);
    27. if(dfn[u]//假如是桥
    28. is_bridge[i]=is_bridge[i^1]=true;//正向边和反向边都是标记为桥,反向边就是i^1
    29. }
    30. else if(i!=(from^1)) low[u]=min(low[u],dfn[j]);//假如正向边不等于转移过来的边,也即不是父节点,则更新
    31. }
    32. if(dfn[u]==low[u])//假如相等了说明找到了一个连通块
    33. {
    34. ++dcc_cnt;//连通块++
    35. int y;
    36. do
    37. {
    38. y=stk[top--];//栈里的元素就是他的连通块元素
    39. id[y]=dcc_cnt;//标记这个点是属于这个连通块的
    40. }while(y!=u);
    41. }
    42. }
    43. int main()
    44. {
    45. cin>>n>>m;
    46. memset(h,-1,sizeof h);
    47. while(m--)
    48. {
    49. int a,b;
    50. cin>>a>>b;
    51. add(a,b),add(b,a);
    52. }
    53. tarjan(1,-1);//搜索一下所有的边
    54. for(int i=0;i//枚举一下所有的边
    55. if(is_bridge[i])//假如这条边是桥
    56. d[id[e[i]]]++;//则则这条边的上一个边连通分量度数++
    57. int cnt=0;
    58. for(int i=1;i<=dcc_cnt;i++)//缩点后的图
    59. if(d[i]==1) cnt++;//算一遍度数为1的边
    60. cout<<(cnt+1)/2<
    61. return 0;
    62. }

    2.点连通分量

    点连通分量(v-DCC):就是删除无向图的一个点,假如该图不连通了,则这个点叫做割点,点连通分量就是极大的不含割点的连通分量

    1.弄连个时间戳dfn表示第一次遍历到这个点的时间戳,low是他的子树中的最小的时间戳

    2.求割点:当low[y]>=dfn[x],

    (1)假如x不是根节点,那么x是割点

    (2)假如是根节点,至少有两个字节点yi使得low[yi]>=dfn[x],则x是割点

    3.用stack存边连通分量,因为假如low[x]==dfn[x],说明找到了这个点的祖宗节点,则入栈的就是他的子树,也就是边连通分量

     

     1.电力

    信息学奥赛一本通(C++版)在线评测系统 (ssoier.cn)

    1.统计连通块的个数cnt

    2.枚举从那个连通块中删,在枚举连通块中删除那个点,假如删除这个点后该连通块变成了s个,则答案就是max(s+cnt-1)

    1. #include
    2. using namespace std;
    3. typedef long long ll;
    4. const int N=10010,M=30010;
    5. int n,m,ans,root;
    6. int h[N],e[M],ne[M],idx;
    7. int dfn[N],low[N],timestamp;
    8. void add(int a,int b)
    9. {
    10. e[idx]=b,ne[idx]=h[a],h[a]=idx++;
    11. }
    12. void tarjan(int u)
    13. {
    14. dfn[u]=low[u]=++timestamp;//第一次遍历让她们都等于时间戳
    15. int cnt=0;//记录删除割点后的连通块个数
    16. for(int i=h[u];~i;i=ne[i])
    17. {
    18. int j=e[i];
    19. if(!dfn[j])//假如没有遍历过
    20. {
    21. tarjan(j);//遍历一遍
    22. low[u]=min(low[u],low[j]);
    23. if(low[j]>=dfn[u]) cnt++;//假如是割点,则连通块++
    24. }
    25. else low[u]=min(low[u],dfn[j]);//更新一遍最小值
    26. }
    27. if(u!=root&&cnt) cnt++;//假如不是根,且连通块个数不为0,说明这个也是一个割点,则连通块个数+1
    28. ans=max(ans,cnt);//更新一遍最大值
    29. }
    30. int main()
    31. {
    32. while(scanf("%d%d",&n,&m),n||m)
    33. {
    34. memset(dfn,0,sizeof dfn);//清空,因为dfn兼具判重的作用
    35. memset(h,-1,sizeof h);//清空
    36. idx=timestamp=0;//清空状态
    37. while(m--)
    38. {
    39. int a,b;
    40. scanf("%d%d",&a,&b);
    41. add(a,b),add(b,a);
    42. }
    43. ans=0;
    44. int cnt=0;
    45. for(root=0;root//枚举每个为根
    46. if(!dfn[root])//假如不在任何一个连通块中
    47. {
    48. cnt++;//则新开个连通块
    49. tarjan(root);//遍历一遍
    50. }
    51. printf("%d\n",cnt+ans-1);
    52. }
    53. return 0;
    54. }

    2.矿场搭建

    396. 矿场搭建 - AcWing题库

    题意:给定一个无向图,问最少在几个点上设置出口,可以使得不管哪个点坍塌,其余所有点都可以与某个出口连通 

     

    1. #include
    2. using namespace std;
    3. typedef unsigned long long ull;
    4. const int N=1010,M=510;
    5. int n,m,root;
    6. int h[N],e[M],ne[M],idx;
    7. int dfn[N],low[N],timestamp;
    8. int stk[N],top;
    9. int id[N],dcc_cnt;
    10. vector<int>dcc[N];
    11. bool cut[N];
    12. void add(int a,int b)
    13. {
    14. e[idx]=b,ne[idx]=h[a],h[a]=idx++;
    15. }
    16. void tarjan(int u)
    17. {
    18. dfn[u]=low[u]=++timestamp;//第一次遍历让她们都等于时间戳
    19. stk[++top]=u;
    20. if(u==root&&h[u]==-1)//假如是孤立点
    21. {
    22. dcc_cnt++;
    23. dcc[dcc_cnt].push_back(u);
    24. return;
    25. }
    26. int cnt=0;//记录子树的个数
    27. for(int i=h[u];~i;i=ne[i])
    28. {
    29. int j=e[i];
    30. if(!dfn[j])//假如没有遍历过
    31. {
    32. tarjan(j);//遍历一遍
    33. low[u]=min(low[u],low[j]);
    34. if(low[j]>=dfn[u])
    35. {
    36. cnt++;//子树加1
    37. if(u!=root||cnt>1) cut[u]=true;//假如不为根或者子树为>1则为割点
    38. ++dcc_cnt;
    39. int y;//求割点中的点,也即点连通块的点,并放进队列中去
    40. do
    41. {
    42. y=stk[top--];
    43. dcc[dcc_cnt].push_back(y);
    44. }while(y!=j);
    45. dcc[dcc_cnt].push_back(u);
    46. }
    47. }
    48. else low[u]=min(low[u],dfn[j]);//更新一遍最小值
    49. }
    50. }
    51. int main()
    52. {
    53. int T=1;
    54. while(cin>>m,m)
    55. {
    56. for(int i=1;i<=dcc_cnt;i++) dcc[i].clear();//清空
    57. idx=n=timestamp=top=dcc_cnt=0;//初始化
    58. memset(cut,0,sizeof cut);//清空割点
    59. memset(dfn,0,sizeof dfn);//清空,因为dfn兼具判重的作用
    60. memset(h,-1,sizeof h);//清空
    61. while(m--)
    62. {
    63. int a,b;
    64. scanf("%d%d",&a,&b);
    65. n=max(n,max(a,b));
    66. add(a,b),add(b,a);
    67. }
    68. for(root=1;root<=n;root++)//枚举每个为根
    69. if(!dfn[root])//假如不在任何一个连通块中
    70. tarjan(root);//遍历一遍
    71. int res=0;
    72. ull num=1;
    73. for(int i=1;i<=dcc_cnt;i++)//枚举每一个连通块
    74. {
    75. int cnt=0;
    76. int len=dcc[i].size();
    77. for(int j=0;j//枚举该连通块的点
    78. if(cut[dcc[i][j]])//假如是割点,则割点++
    79. cnt++;
    80. if(cnt==0&&len>1) res+=2,num*=len*(len-1)/2;//假如不是割点且点的数量大于1
    81. else if(cnt==0) res++;//假如不是割点点的数量为1,则为孤立点
    82. else if(cnt==1) res++,num*=len-1;//假如有一个割点
    83. }
    84. printf("Case %d: %d %llu\n",T++,res,num);
    85. }
    86. return 0;
    87. }
  • 相关阅读:
    森林防火(资源监管)“空天地人”四位一体监测系统方案
    HTML点击链接强制触发下载
    【LeetCode每日一题】——面试题17.16.按摩师
    applicationContext.xml
    FT232替代GP232RL USB-RS232转换器芯片国产化应用
    全网最硬核 JVM TLAB 分析 3. JVM EMA期望算法与TLAB相关JVM启动参数
    Teamcity NuGet Installer
    专业的人做专业的事 GBASE参编数据库发展研究报告(2022年)、入选全球数据库产业图谱
    【云原生之Docker实战】使用Docker部署Navidrome家庭个人音乐库
    @ApiModel 和 @ApiModelProperty
  • 原文地址:https://blog.csdn.net/m0_63729880/article/details/126442761