割点 割边代码相似,稍作修改即可
区别(个人理解):
割点:需要单独判断根是否为割点,有至少2个child即为割点
判断:low[v]>=dfn[u] (u为父,v为子)
割边:
判断:low[v]>dfn[u];(不用考虑根节点)
题目描述
给出一个 nnn 个点,mmm 条边的无向图,求图的割点。
输入格式
第一行输入两个正整数 n,mn,mn,m。
下面 mmm 行每行输入两个正整数 x,yx,yx,y 表示 xxx 到 yyy 有一条边。
输出格式
第一行输出割点个数。
第二行按照节点编号从小到大输出节点,用空格隔开。
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <set>
using namespace std;
const int maxn=20050;
int n,m,a,b,low[maxn],dfn[maxn],cnt=0,ans=0;
vector<int> g[maxn];
bool vis[maxn]={false}; // true代表为割点
void cut(int u,int father)
{
low[u]=dfn[u]=++cnt;
int child=0; //记录根的孩子个数
for(int i=0;i<g[u].size();i++)
{
int v=g[u][i];
if(dfn[v]) // v已访问(可能为u的祖先)更新u的low
low[u]=min(low[u],dfn[v]);
if(!dfn[v]) // v没访问,dfs
{
cut(v,u);
low[u]=min(low[u],low[v]); //回溯更新u的low(v可以到达过u的祖先并且更新low,而u连接v,u的low值更新(个人理解))
if(dfn[u] <= low[v] && u!=father) //根是否为割点要由child个数判断
vis[u]=1; /* v的最小时间戳如果比u大,就代表v不能
绕过u到达u的祖先 即为割点 */
if(u==father)
child++; //u为父亲,孩子增加1
}
}
if(child>1 && u==father) //如果是根,并且有至少2个孩子,就是割点
vis[u]=1;
}
int main()
{
cin>>n>>m;
while(m--)
{
cin>>a>>b;
g[a].push_back(b); //存图
g[b].push_back(a);
}
for(int i=1;i<=n;i++) //图可能不是连通图,所以一次循环遍历
if(!dfn[i]) cut(i,i);
for(int i=1;i<=n;i++)
if(vis[i]) ans++; //计数
cout<<ans<<endl;
for(int i=1;i<=n;i++)
if(vis[i]) cout<<i<<" "; //输出答案
return 0; //愉快的水完一题
}