目录
名词解释
割点:在无向图中,删除某个节点后,图的连通分量数量增加,则称该节点为割点
桥:如果删除某条边后,连通图变得不再连通,则此条边为桥,或者为割边
Tarjan算法
在Tarjan算法中,有两个十分重要的数组,dfn数组,low数组
dfn数组:表示dfs遍历到该节点的序号,也就是顺序值
low数组:表示当前顶点不通过父亲节点能访问到的祖先节点(父亲节点上面的节点)中的最小顺序值
割点求解:
如果,至少存在一个儿子节点必须要经过父亲节点才能访问到祖先节点,那么这个父亲节点即为割点,假设父亲节点为u,儿子节点为v,那么满足:low[v] >= dfn[u] 说明:节点u为割点,但是仍然存在一种情况,当u为根节点时,所有儿子节点的low[v] 一定满足大于等于dfn[u],所以我们必须要分开讨论:如果,根节点必须要有两个儿子节点,就可以说明跟节点为割点
割边求解:
相同,割边的话,儿子节点不经过这条边就可以访问到祖先节点,那么就要满足:low[v] > dfn[u],如果low[v] == dfn[u],节点v还可以通过其他路径可以回到u,但是只包含条件low[x] > dfn[u]时,说明没有任何一条路可以出了u-v边外,由u到达v,此时说明,u-v之间的边为一条割边
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
typedef long long ll;
const int maxn = 10000;
int low[maxn],dfn[maxn],head[maxn];
int n,m,tot,k,root;
bool flag[maxn];
vector<pair<int,int> > bridge;
struct Node
{
int next,v;
}edge[maxn];
void Add_edge(int x,int y)
{
edge[k].v = y;edge[k].next = head[x];head[x] = k++;
edge[k].v = x;edge[k].next = head[y];head[y] = k++;
}
void Tarjan(int x,int father)
{
int child = 0;
dfn[x] = low[x] = ++tot;
for(int i = head[x];i != -1;i = edge[i].next)
{
int v = edge[i].v;
if(!dfn[v])
{
child ++;
Tarjan(v,x);
low[x] = min(low[x],low[v]);
if(x != root && dfn[x] <= low[v]) flag[x] = 1; //表示当前节点为割点
if(x == root && child == 2) flag[x] = 1;
if(low[v] > dfn[x]) bridge.push_back(make_pair(x,v));
}
else if(v != father)
low[x] = min(dfn[v],low[x]);
}
}
int main()
{
memset(head,-1,sizeof(head));
int x,y;
scanf("%d%d",&n,&m);
for(int i = 0;i < m;i ++)
{
scanf("%d%d",&x,&y);
Add_edge(x,y);
}
root = 1; //在求解割点、割边的时候首先确保图为连通图
Tarjan(1,root);
printf("图中的割点为:\n");
for(int i = 1;i <= n ;i ++)
if(flag[i])
printf("%d ",i);
printf("\n");
printf("图中的割边为:\n");
for(int i = 0;i < bridge.size();i ++)
printf("%d -- %d\n",bridge[i].first,bridge[i].second);
printf("\n");
return 0;
}
/*
6 7
1 4
1 3
4 2
3 2
2 5
2 6
5 6
图中的割点为:
2
图中的割边为:
*/