【图论——割点与桥】——洛谷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;
}

猜你喜欢

转载自blog.csdn.net/qq_41958857/article/details/80061324
今日推荐