Luogu3388:利用Tarjan求无向图的割点
割点就是维护双连通分量的一个点,如果删去的话,原本的双连通分量就会被拆成若干个连通分量
利用Tarjan算法可以求出无向图的所有割点
下面介绍一下:
int n,m,cnt,deep,root,ans; int g[maxn],dfn[maxn],low[maxn],iscut[maxn]; struct Edge{int t,w,next;}e[maxm];
邻接表建图,开二倍边长(无向图)
deep用来跟踪记录每一个点的深度,或者说,它在dfs中是第几个访问的
然后root是用来给当前进行dfs的连通分量指定的一个根
dfn数组用来记录当前点在dfs中是第几个被搜到的,low数组用来记录这个点及其子孙节点所连的所有节点中,dfn的最小值
由于本题不用求强连通分量,不用开栈记录了
iscut的意思很显然
for(int i=1;i<=n;i++) if(!dfn[i]) {root=i;tarjan(i,-1);}
建图后对于每一个连通分量,进行一次Tarjan求割点
Tarjan算法如下:
int tarjan(int u,int fa) { int child=0,lowu; lowu=dfn[u]=++deep; for(int tmp=g[u];tmp;tmp=e[tmp].next) { int v=e[tmp].t; if(!dfn[v]) { child++; int lowv=tarjan(v,u); lowu=min(lowu,lowv); if(lowv>dfn[u]) iscut[u]=1; } else if(v!=fa&&dfn[v]<dfn[u]) lowu=min(lowu,dfn[v]); } if(fa<0&&child==1) iscut[u]=false; low[u]=lowu; return lowu; }
具体原理和求强连通的Tarjan很类似(其实Tarjan就是一类东西。。。)
先不介绍了以后再说
下面给出完整的实现,题是洛谷上的一个板子题,然后我再洛谷上发现了不少神奇的板子,真是贴心
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 const int maxn=100005; 5 const int maxm=200005; 6 int n,m,cnt,deep,root,ans; 7 int g[maxn],dfn[maxn],low[maxn],iscut[maxn]; 8 struct Edge{int t,w,next;}e[maxm]; 9 void addedge(int u,int v,int w) 10 { 11 e[++cnt].t=v;e[cnt].w=w; 12 e[cnt].next=g[u];g[u]=cnt; 13 } 14 int tarjan(int u,int fa) 15 { 16 int child=0,lowu; 17 lowu=dfn[u]=++deep; 18 for(int tmp=g[u];tmp;tmp=e[tmp].next) 19 { 20 int v=e[tmp].t; 21 if(!dfn[v]) 22 { 23 child++; 24 int lowv=tarjan(v,u); 25 lowu=min(lowu,lowv); 26 if(lowv>dfn[u]) iscut[u]=1; 27 } 28 else if(v!=fa&&dfn[v]<dfn[u]) 29 lowu=min(lowu,dfn[v]); 30 } 31 if(fa<0&&child==1) iscut[u]=false; 32 low[u]=lowu; 33 return lowu; 34 } 35 int main() 36 { 37 scanf("%d%d",&n,&m); 38 int u,v; 39 for(int i=1;i<=m;i++) 40 { 41 scanf("%d%d",&u,&v); 42 addedge(u,v,1);addedge(v,u,1); 43 } 44 for(int i=1;i<=n;i++) 45 if(!dfn[i]) {root=i;tarjan(i,-1);} 46 for(int i=1;i<=n;i++) 47 if(iscut[i]) ans++; 48 printf("%d ",ans); 49 for(int i=1;i<=n;i++) 50 if(iscut[i]) printf("%d ",i); 51 return 0; 52 }
目前的程度,先能调用API就够了