割点、割边(桥)

emmm,拖了好久的割点割边,今天终于有时间来写一下模板了233

 

【定义】

在一个无向图中,如果有一个顶点集合,删除这个顶点集合以及这个集合中所有顶点相关联的边以后,图的连通分量增多,就称这个点集为割点集合,如果某个割点集合只含有一个顶点 X(也即{X}是一个割点集合),那么X称为一个割点

类似的,在一个无向图中,如果有一个边集合,删除这个边集合以后,图的连通分量增多,就称这个边集为割边集合,如果某个割边集合只含有一条边 X(也即{X}是一个边集合),那么X称为一个割边,也叫做

 

【Tarjan算法】

对没错,Tarjan算法就可以用来求割点割边

我们先回顾一下Tarjan算法的精髓--- dfn 和 low

  • dfn(x)为节点 x 搜索的次序编号(时间戳);
  • low(x)为 x 或 x 的子树能够追溯到的最早的祖先的次序号。

【割点】

对于一个点 u,有以下两种情况:

  1. 如果 u 是根节点,那么当它有多于一个子树时,它就是割点
  2. 如果 u 不是根节点,并且 u 为 v 在搜索树中的父亲,当 dfn[ u ] ≤ low[ v ] 时,它就是割点

【割边】

一条边(u,v)是割边,当且仅当 u 为 v 在搜索树中的父亲,并且 dfn[ u ] < low[ v ]

在实现的时候,由于有重边的存在,我们还要用一个数组记录一下树枝边

【更新 low 值】

起初,low[ u ] = dfn [ u ],表示能追溯到的最早祖先就是 u

现有一条边(u,v)

如果 dfn[ v ] == 0,那么就继续dfs,回溯的时候 low[ u ] = min ( low[ u ] , low[ v ] )

如果 dfn[ v ] != 0,那么 low[ u ] = min ( low[ u ] , dfn[ v ] )

 

【模板】

割点模板

这道题就是道纯模板题

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
#define M 200005
using namespace std;
int n,m,t,num,root;
int first[N],v[M],next[M];
int dfn[N],low[N],point[N];
bool cut[N];
void add(int x,int y)
{
    t++;
    next[t]=first[x];
    first[x]=t;
    v[t]=y;
}
void Tarjan(int x)
{
    int i,j,children=0;
    num++;
    dfn[x]=num;
    low[x]=num;
    for(i=first[x];i;i=next[i])
    {
        j=v[i];
        if(!dfn[j])
        {
            children++;
            Tarjan(j);
            low[x]=min(low[x],low[j]);
            if(x==root&&children>1)  cut[x]=true;
            if(dfn[x]<=low[j]&&x!=root)  cut[x]=true;
        }
        else
          low[x]=min(low[x],dfn[j]);
    }
}
int main()
{
    int x,y,i,j,ans=0;
    scanf("%d%d",&n,&m);
    for(i=1;i<=m;++i)
    {
        scanf("%d%d",&x,&y);
        add(x,y);
        add(y,x);
    }
    for(i=1;i<=n;++i)
      if(!dfn[i])
        root=i,Tarjan(i);
    for(i=1;i<=n;++i)
      if(cut[i])
        point[++ans]=i;
    printf("%d\n",ans);
    for(i=1;i<=ans;++i)
      printf("%d ",point[i]);
    return 0;
}

割边模板

这道题的意思是,给出一个有桥图,我们要加边将它变成边双联通图,求最少要加几条边

具体做法就是将所有边双联通都缩为一个点,将缩完点的图看做一棵树,求出叶节点的个数 ans

最后的答案就是(ans + 1)/ 2

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 5005
#define M 20005
using namespace std;
int n,m,num,t=1;
int first[N],u[M],v[M],next[M];
int du[N],dfn[N],low[N],from[N],father[N];
bool bridge[M];
int find(int x)
{
	if(father[x]!=x)
	  father[x]=find(father[x]);
	return father[x];
}
void add(int x,int y)
{
	t++;
	next[t]=first[x];
	first[x]=t;
	u[t]=x;
	v[t]=y;
}
void Tarjan(int x)
{
	int i,j;
	num++;
	dfn[x]=num;
	low[x]=num;
	for(i=first[x];i;i=next[i])
	{
		j=v[i];
		if(i==(from[x]^1))
		  continue;
		if(!dfn[j])
		{
			from[j]=i;
			Tarjan(j);
			low[x]=min(low[x],low[j]);
			if(dfn[x]<low[j])
			{
				bridge[from[j]]=true;
				bridge[from[j]^1]=true;
			}
		}
		else
		  low[x]=min(low[x],dfn[j]);
	}
}
int main()
{
	int x,y,i,j,ans=0;
	scanf("%d%d",&n,&m);
	for(i=1;i<=m;++i)
	{
		scanf("%d%d",&x,&y);
		add(x,y);
		add(y,x);
	}
	Tarjan(1);
	for(i=1;i<=n;++i)
	  father[i]=i;
	for(i=2;i<=t;i+=2)
	  if(!bridge[i])
	    father[find(u[i])]=find(v[i]);
	for(i=2;i<=t;i+=2)
	  if(bridge[i])
	    du[find(u[i])]++,du[find(v[i])]++;
	for(i=1;i<=n;++i)
	  if(find(i)==i&&du[i]==1)
	    ans++;
	printf("%d",(ans+1)/2);
	return 0;
}

 

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值