“ 海绵宝宝与派大星的算法题解:第二篇 ”
Tarjan用于求割点
01
—
洛谷 P3388 【模板】割点(割顶)
P1195 口袋的天
思路:用两个数组来记录访问的序号,dfn[u]表示第一次访问到u节点的顺序,low[u]表示通过子节点可以回溯到的最小顺序,如果非根节点可通过子节点更新的low[u]小于它本身及有回路,反之无回路。当[u]结点是根节点,且有两棵及以上子树时其必是割点。
#includeusing namespace std;struct node{ int nxt,mark;}pre[200020];int n,m,idx,cnt,tot;int head[200020],dfn[200020],low[200020];bool cut[200020];void add(int u,int v){ pre[++cnt].nxt=v; pre[cnt].mark=head[u]; head[u]=cnt;}void tarjan(int u,int fa){ dfn[u]=low[u]=++idx; int child=0; for(int i=head[u];i!=0;i=pre[i].mark){ int nx=pre[i].nxt; if(!dfn[nx]){ tarjan(nx,fa); low[u]=min(low[u],low[nx]);//重点 if(low[nx]>=dfn[u]&&u!=fa) cut[u]=1; if(u==fa) child++; } low[u]=min(low[u],dfn[nx]);//重点 不能为low[u]=min(low[u],low[nx]) 8字形时错误 } if(child>=2&&u==fa) cut[u]=1;}int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ int a,b; scanf("%d%d",&a,&b); add(a,b); add(b,a); } for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i,i); for(int i=1;i<=n;i++) if(cut[i]) tot++; printf("%d\n",tot); for(int i=1;i<=n;i++) if(cut[i]) printf("%d ",i); return 0;}
与君共勉