題目背景
割點
題目描述
給出一個n個點,m條邊的無向圖,求圖的割點。
輸入輸出格式
輸入格式:
第一行輸入n,m
下面m行每行輸入x,y表示x到y有一條邊
輸出格式:
第一行輸出割點個數
第二行按照節點編號從小到大輸出節點,用空格隔開
輸入輸出樣例
輸入樣例#1:
6 7
1 2
1 3
1 4
2 5
3 5
4 5
5 6
輸出樣例#1:
1
5
說明
n,m均爲100000
tarjan 圖不一定聯通!!!
思路:模板題
割點定義:在一個無向連通圖中刪去這個點,圖會變得不連通,則該點爲割點。
題解:
#include<iostream>
#include<cstdio>
#include<stack>
using namespace std;
const int maxn=200000+10;
struct cc{
int from,to;
}es[maxn];
int first[maxn],nxt[maxn];
int tot=0;
void build(int ff,int tt)
{
es[++tot]=(cc){ff,tt};
nxt[tot]=first[ff];
first[ff]=tot;
}
int dfn[maxn],low[maxn];
int cnt=0,scc_cnt=0;
int pd[maxn];
bool vis[maxn];
int dfs(int u,int fa)
{
low[u]=dfn[u]=++cnt;
int cd=0;//統計兒子個數
for(int i=first[u];i;i=nxt[i])
{
int v=es[i].to;
if(!dfn[v])
{
cd++;
low[v]=dfs(v,u);
low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u])
//如果u的子樹中最早能訪問到的結點都比他後訪問,則u可能是割點
{
if(!vis[u])
{
pd[u]=1;
vis[u]=1;
}
}
}
else if(v!=fa)
{
low[u]=min(low[u],dfn[v]);
}
}
if(fa==-1&&cd==1)//如果是該點是根節點且只連向一個點,則不是割點
{
pd[u]=0;
}
return low[u];
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
build(u,v);
build(v,u);
}
for(int i=1;i<=n;i++)
{
if(!dfn[i])
{
dfs(i,-1);
}
}
int ans=0;
for(int i=1;i<=n;i++)
{
if(pd[i])
{
ans++;
}
}
printf("%d\n",ans);
for(int i=1;i<=n;i++)
{
if(pd[i])
{
printf("%d ",i);
}
}
return 0;
}