1. 问题描述:

给定一个由 n 个点 m 条边构成的无向图,请你求出该图删除一个点之后,连通块最多有多少。

输入格式

输入包含多组数据。每组数据第一行包含两个整数 n,m。接下来 m 行,每行包含两个整数 a,b,表示 a,b 两点之间有边连接。数据保证无重边。点的编号从 0 到 n−1。读入以一行 0 0 结束。

输出格式

每组数据输出一个结果,占一行,表示连通块的最大数量。

数据范围

1 ≤ n ≤ 10000,
0 ≤ m ≤15000,
0 ≤ a,b < n

输入样例:

3 3
0 1
0 2
2 1
4 2
0 1
2 3
3 1
1 0
0 0

输出样例:

1
2
2
来源:https://www.acwing.com/problem/content/description/1185/

2. 思路分析:

分析题目可以知道我们可以先统计出连通块的数目count,枚举从哪一个连通块中删除点,然后再枚举删除哪一个点,计算删除当前点之后可以得到的的连通块数目s,则最终的答案为s + count -  1,所以问题的核心是如何枚举删除某个连通块中的每一个点并且计算出s,我们其实可以使用求解割点的方法来解决,割点属于点的双连通分量的内容,我们可以使用tarjan算法来求解割点,类似于之前无向图的边双连通分量算法,需要借助一个时间戳timestamp和两个数组dfn,low,其中dfn[u]表示dfs遍历到节点u对应的时间戳,low[u]表示从节点u出发往下走能够遍历到的最早的时间戳,我们主要是分为两种情况结合递归调用完每一个子节点y之后的low[y] >= dfn[x]判断是否成立来计算连通块的数目:

  • x不是根节点,删除节点x之后那么x的子树与x的父节点对应的连通块是不连通的,连通块的数目在原来删除节点x之后的子节点对应的连通块数目上加1
  • x是根节点,那么去掉根节点x之后与删掉当前的根节点得到的子节点的连通块的数目还是一样的,也即没有影响

我们只需要递归调用完当前的节点u的某一个子节点next之后判断low[next] >= dfn[u]是否成立,如果成立说明当前的节点u是一个割点,那么连通块的数目加1,当我们遍历完根节点u的所有子节点之后那么表示枚举删除节点u之后得到的子节点的连通块的数目已经求解出来了,此时还需要判断一下当前删除的节点是否是父节点,如果不是父节点,还需要加上u的父节点对应的连通块,也即连通块数目需要加1。

因为节点编号为0 ~ n - 1,所以我们可以遍历每一个节点,以每一个点作为根节点调用tarjan算法,每调用一次那么连通块的数目加1,循环结束那么就可以计算出原图中总共的连通块的数目,并且每调用一次tarjan方法,其实上枚举的是以当前的节点编号作为根节点对应的连通块中的所有节点,因为tarjan算法基于dfs,所以可以在遍历每一个节点的时候枚举删除当前的节点,因为需要根据子节点的low[next]与当前根节点的dfn[u]的大小关系判断当前的删除的节点是否是割点,所以需要在递归调用tarjan算法之后来判断dfn[u]与low[next]的大小关系,这样实际上是枚举删除某个连通块中的某个节点,而dfs会遍历所有的节点这样就可以在某一个连通块中枚举删除每一个节点得到的对应的连通块数目,在所有的情况中取一个max就是当前连通块删除一个点之后得到的连通块的最大数目。

3. 代码如下:

c++代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>using namespace std;const int N = 10010, M = 30010;int n, m;
int h[N], e[M], ne[M], idx;
int dfn[N], low[N], timestamp;
int root, ans;void add(int a, int b)
{e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}void tarjan(int u)
{dfn[u] = low[u] = ++ timestamp;int cnt = 0;for (int i = h[u]; ~i; i = ne[i]){int j = e[i];if (!dfn[j]){tarjan(j);low[u] = min(low[u], low[j]);if (low[j] >= dfn[u]) cnt ++ ;}else low[u] = min(low[u], dfn[j]);}if (u != root && cnt) cnt ++ ;ans = max(ans, cnt);
}int main()
{while (scanf("%d%d", &n, &m), n || m){memset(dfn, 0, sizeof dfn);memset(h, -1, sizeof h);idx = timestamp = 0;while (m -- ){int a, b;scanf("%d%d", &a, &b);add(a, b), add(b, a);}ans = 0;int cnt = 0;for (root = 0; root < n; root ++ )if (!dfn[root]){cnt ++ ;tarjan(root);}printf("%d\n", ans + cnt - 1);}return 0;
}

python代码(只有一个数据运行超过1s超时了):

from typing import List
import sysclass Solution:root, timestamp, res = None, None, Nonedef tarjan(self, u: int, dfn: List[int], low: List[int], g: List[List[int]]):dfn[u] = low[u] = self.timestamp + 1self.timestamp += 1count = 0for next in g[u]:if dfn[next] == 0:self.tarjan(next, dfn, low, g)low[u] = min(low[u], low[next])# 结合后面直接更新low[u]的判断所以这里可以取等号if low[next] >= dfn[u]: count += 1else:low[u] = min(low[u], dfn[next])if u != self.root: count += 1# 这里并不需要求解双连通分量, 枚举完删除当前子节点之后的连通块数目然后更新res即可self.res = max(self.res, count)def process(self):while True:n, m = map(int, input().split())if n == 0 and m == 0: breakg = [list() for i in range(n + 10)]for i in range(m):a, b = map(int, input().split())g[a].append(b)g[b].append(a)dfn, low = [0] * (n + 10), [0] * (n + 10)# count计算原图中连通块的数目count = self.res = self.timestamp = 0# 枚举当前的根节点for i in range(n):# 枚举每一个连通块if dfn[i] == 0:count += 1# 当前的根节点是iself.root = iself.tarjan(i, dfn, low, g)print(self.res + count - 1)if __name__ == "__main__":# 设置递归最大调用次数sys.setrecursionlimit(5000)Solution().process()

1183 电力(点的双连通分量--求解割点)相关推荐

  1. 无向图的双连通分量(一)

    目录 无向图的双连通分量 概念 ①边双连通分量 e-DCC 桥: ②点双连通分量 v-DCC 割点: e-DCC Tarjan算法 判断当前边是否为桥: 寻找e-DCC: 例题 代码 tarjan算法 ...

  2. Tarjan算法应用 (割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)问题)...

    转载自:http://hi.baidu.com/lydrainbowcat/blog/item/2194090a96bbed2db1351de8.html 基本概念: 1.割点:若删掉某点后,原连通图 ...

  3. 学习有向图和无向图的强连通分量(基本概念+割点+点双联通分量+桥+边双连通分量+全套模板【Tarjan】)

    最近总是考到Tarjan,让我措手不及 基本概念 割点以及点双连通分量 Tarjan法求割点 推导过程 代码实现 Tarjan法求点双连通分量 推导过程 代码实现 有向图的Tarjan缩点 桥与边双连 ...

  4. POJ 1523 SPF (割点 点双连通分量)

    题意:求出割点以及除去割点后的连通分量的数量(附带求出了点双连通分量(块)) [求割点]对图深度优先搜索,定义DFS(u)为u在搜索树(以下简称为树)中被遍历到的次序号.定义Low(u)为u或u的子树 ...

  5. Tarjan算法求解桥和边双连通分量(附POJ 3352 Road Construction解题报告)

    http://blog.csdn.net/geniusluzh/article/details/6619575 在说Tarjan算法解决桥和边双连通分量问题之前我们先来回顾一下Tarjan算法是如何求 ...

  6. 算法笔记--无向图的桥、割点、边双连通分量和点双连通分量

    概念: 桥:无向图中删去一条边使得图不再联通,则这条边称为桥 割点:无向图中删去一个点使得图不再联通,则这个点称为割点 算法: 运用到tarjan算法 关于tarjan算法: https://www. ...

  7. tarjan算法与无向图的连通性(割点,桥,双连通分量,缩点)

    基本概念 给定无向连通图G = (V, E) 割点: 对于x∈V,从图中删去节点x以及所有与x关联的边之后,G分裂为两个或两个以上不相连的子图,则称x为割点 割边(桥) 若对于e∈E,从图中删去边e之 ...

  8. 【图论专题】无向图的双连通分量

    A.AcWing 395. 冗余路径 结论:变成边双连通分量所需要新建的边数 无向图 连多少条边使得整个无向图变成任意两个点之间都有两条完全不相同的路径. 结论1:任意两个点之间都有两条完全不相同的路 ...

  9. 无向图——双连通分量

    双连通图:在无向图连通图中,如果删除该图中的任意一点和依附它的边,不改变图的连通性,则称该图为双连通的无向图. 由上述定义可知,双连通分量中,每两个结点之间至少有两条不同的路径可以相互到达. 割点:在 ...

  10. 强连通分量/点双连通分量/边双联通分量 总结

    前言 % 被某brz逼着问,觉得很有必要好好复习一下这 些 毒瘤东西. 定义 % 连通 如果有向图中的两点 uuu,vvv 间同时存在 uuu 到 vvv 的路径及 vvv 到 uuu 的路径,则称点 ...

最新文章

  1. 数学之美 系列七 -- 信息论在信息处理中的应用
  2. DotNetNuke 框架总揽
  3. jquery ajax post 传递数组 ,多checkbox 取值
  4. 从海康录像机取RTSP转发流,规律性的断流
  5. thinkphp --- 写入日志
  6. 基于OpenGL的贪吃蛇游戏设计与实现
  7. PSpice 正版软件的下载方式
  8. 超详细!在我的世界(MC)中使用cocricot模组搭建一个咖啡厅
  9. 【特效】UE4 Niagara 制作爆炸特效
  10. 彻底解决mac os 下javac Java命令行工具乱码问题
  11. 回归分析什么时候取对数_为什么相关或回归分析时 x和y取log
  12. 键盘输入平方(m²)或立方(m³)等特殊字符
  13. SpringCloud详细教程(上)
  14. 互联网视频直播点播EasyDSS平台如何通过接口设置录像计划?
  15. Python+selenium+360浏览器实现自动测试
  16. 北京市社会保险办理流程
  17. CSS核心概念一把梭-基础部分
  18. 使用Keras进行深度学习:(六)LSTM和双向LSTM讲解及实践
  19. 祖传代码如何优化性能?
  20. Ubuntu 18.04 更换最优软件更新源

热门文章

  1. adams参数化优化设计例子
  2. 软技能:程序员如何在职场上少走弯路
  3. prefetch_related和select_related的区别
  4. 牛客网--23803--DongDong认亲戚
  5. 计算机中h是几进制,16进制后面用H表示,其他进制的用什么表示
  6. dnf单机版 不显示服务器,dnf单机云服务器
  7. c++小游戏——打飞机
  8. web 基于jquery和canvas的打飞机小游戏
  9. 鸿蒙系统清理垃圾,极速清理系统垃圾 一举收回上G磁盘空间
  10. 打发时间的网站,收藏起来吃鸡玩腻了玩玩这些,够你玩一年