【图论——割点与桥】——洛谷P3388【模板】割点(割顶)

此处用u表示这个节点,用v表示u的子节点,fa表示u的父亲节点

pre[u]表示dfs中u这个点被第几个扫到

用low[u]表示u能到的v中pre[v]的最小值

割点:如果low[v]<=pre[u]就证明u这个点的子节点没有办法到达u的父亲节点也就证明去掉这个点就会产生一个连通分量(多一个不相连的图)

利用这个性质就可以求割点

桥:方法与割点相同判断时不同low[v]<pre[u],少了一个等于号。这是因为我们的图中如果v这个节点可以连接到u这个节点那么即使去除u到v这条边与无法多产生一个连通分量

判断时要保证其不沿着原路返回

dfs就是将u的所有边走一遍

pre[v]==0 走上述操作

pre[v]!=0 判断如果是之前扫过的点为了保证low[u]最小让lo[u]=pre[v];

最后还要加一个特判

如果这是第一个点且只有一个直接相连的子节点那么即使去了这个点也没有多产生连通分量,所以我们不将它判定为割点。

//此处程序为xoj.red上的题目,是既求割点又求桥的

#include<cstdio>

#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
int n,m;
vector <int> g[100001];
vector <int> clearg[100001];
int low[100001];
int pre[100001];
bool cut[100001];
int ctz=0;
int ansq=0;
int dfs(int u,int fa)
{
int lowu=++ctz;
pre[u]=ctz;
int child=0;
for(int i=0;i<g[u].size();i++)
{
int v=g[u][i];
if(pre[v]==0)
{
child++;
int lowv=dfs(v,u);
lowu=min(lowu,lowv);
if(lowv>=pre[u])
{
if(lowv>pre[u])
{
ansq++;
}
cut[u]=1;
}
}
else
{
if(pre[v]<pre[u]&&v!=fa)
{
lowu=min(lowu,pre[v]);
}
}
}
if(fa==0&&child==1)
{
cut[u]=0;
}
low[u]=lowu;
return lowu;
}
int main()
{
memset(low,0,sizeof(low));
memset(pre,0,sizeof(pre));
memset(cut,0,sizeof(cut));
while(1)
{
memset(low,0,sizeof(low));
memset(pre,0,sizeof(pre));
memset(cut,0,sizeof(cut));
ansq=0;
for(int i=1;i<=100001;i++)
{
g[i]=clearg[i];
}
scanf("%d %d",&n,&m);
if(n==0&&m==0)
{
return 0;
}
for(int i=1;i<=m;i++)
{
int x,y,w;
scanf("%d %d",&x,&y);
g[x].push_back(y);
g[y].push_back(x);
}
for(int i=1;i<=n;i++)
{
if(pre[i]==0)
{
dfs(i,0);
}
}
int ans=0;
for(int i=1;i<=n;i++)
{
if(cut[i]==1)
{
ans++;
}
}
printf("%d %d\n",ans,ansq);
for(int i=1;i<=n;i++)
{
if(cut[i]==1)
{
printf("%d\n",i);
}
}
}
return 0;
}

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值