题目大意:
给你一个无向图,求其中割点的个数目。
输入数据
第一行一个 n 代表有 n 个点
接下来有多行,一直到读入一个 0,算整个地图的读入结束,再读入一个0,文件数据结束。
每行有第一个数字a,代表接下来的数字都 和 a 相连。
知识汇总:
割点:无向连通图中,如果删除某点后,图变成不连通了,则称该点为割点。
这里割点 和 桥 都是无向图里的概念,大家在这里不要混淆了。
求割点
一个顶点u是割点,当且仅当满足(1)或(2)
(1) u为树根,且u有多于一个子树。
(2) u不为树根,且满足存在(u,v)为树枝边(或称 父子边,即u为v在搜索树中的父亲),使得 dfn(u)<=low(v)。(也就是说 V 没办法绕过 u 点到达比 u dfn要小的点)
注:这里所说的树是指,DFS下的搜索树。
求割点 tarjan里 low 和 dfn
dfn[u]定义和前面类似,但是low[u]定义为u
或者u的子树中能够通过非父子边追溯到的
最早的节点的DFS开始时间
在Tarjan算法求割点我们要加一个数组 Father[u], 判断两者是否是父子边
#include<cstdio> #include<cstdlib> #include<cmath> #include<iostream> #include<algorithm> #include<cstring> #include<vector> using namespace std; #define maxn 10005 int dfn[maxn];///代表最先遍历到这个点的时间 int low[maxn];///这个点所能到达之前最早的时间点 int Father[maxn];///保存这个节点的父亲节点 int n, m, Time, top;///Time 时间点, top用于栈操作 vector<vector<int> > G; void Init() { G.clear(); G.resize(n+1); memset(low, 0, sizeof(low)); memset(dfn, 0, sizeof(dfn)); memset(Father, 0, sizeof(Father)); Time = 0; } void Tarjan(int u,int fa) { low[u] = dfn[u] = ++Time; Father[u] = fa; int len = G[u].size(), v; for(int i=0; i<len; i++) { v = G[u][i]; if(!dfn[v]) { Tarjan(v, u); low[u] = min(low[u], low[v]); } else if(fa != v)///假如我们在这里写上了 low[u] = min(low[v], low[u]),那么就相当于我们由v回了v之前的节点 low[u] = min(dfn[v], low[u]); } } void solve() {/** 求割点 一个顶点u是割点,当且仅当满足(1)或(2) (1) u为树根,且u有多于一个子树。 (2) u不为树根,且满足存在(u,v)为树枝边(或称 父子边,即u为v在搜索树中的父亲),使得 dfn(u)<=low(v)。 (也就是说 V 没办法绕过 u 点到达比 u dfn要小的点) 注:这里所说的树是指,DFS下的搜索树*/ int RootSon = 0, ans = 0;///根节点儿子的数量 bool Cut[maxn] = {false};///标记数组,判断这个点是否是割点 Tarjan(1,0); for(int i=2; i<=n; i++) { int v = Father[i]; if(v == 1)///也是就说 i的父亲是根节点 RootSon ++; else if(dfn[v] <= low[i]) Cut[v] = true; } for(int i=2; i<=n; i++) { if(Cut[i]) ans ++; } if(RootSon > 1) ans++; printf("%d ", ans); } int main() { while(scanf("%d", &n), n) { int a, b; char ch; Init(); while(scanf("%d", &a), a) { while(scanf("%d%c",&b,&ch)) { G[a].push_back(b); G[b].push_back(a); if(ch == ' ') break; } } solve(); } return 0; }