D17 割边 Tarjan 算法
D17 割边 Tarjan 算法_哔哩哔哩_bilibili
割边(桥)
对于一个无向图,如果删掉一条边后图中的连通分量数增加了,则称这条边为桥或者割边.
过程
和割点差不多,只要改一处:𝑙𝑜𝑤𝑣 >𝑑𝑓𝑛𝑢
就可以了,而且不需要考虑根节点的问题.
如果 𝑙𝑜𝑤𝑣 ≤𝑑𝑓𝑛𝑢
表示还可以回到父节点或祖先,如果顶点 𝑣
不能回到祖先也没有另外一条回到父亲的路,那么 𝑢 −𝑣
这条边就是割边.
割点判定:low[v]>=dfn[u],允许走反边更新 low 值.
割边判定:low[v]>dfn[u],不允许走反边更新 low 值.
判反边:链式前向星建图,$i!=(e^\wedge 1)$.这种判法 有重边 时也可以.
#include<bits/stdc++.h> using namespace std; const int N=50005,M=600005; int n,m; int h[N],to[M],ne[M],idx=1; //从2,3开始配对 void add(int a,int b){ to[++idx]=b;ne[idx]=h[a];h[a]=idx; } int dfn[N],low[N],tim,cnt; struct bridge{ int x,y; bool operator<(const bridge &t)const{ return (x==t.x) ? y<t.y:x<t.x; } }bri[M]; //桥 void tarjan(int u,int e){ dfn[u]=low[u]=++tim; for(int i=h[u];i;i=ne[i]){ int v=to[i]; if(!dfn[v]){ //若v未访问 tarjan(v,i); low[u]=min(low[u],low[v]); if(low[v]>dfn[u]) bri[cnt++]={u,v}; //若low[v]≤dfn[u],说明v可以绕回u或祖先 } else if(i!=(e^1)) //若v已访问且不是反边 low[u]=min(low[u],dfn[v]); } } int main(){ cin>>n>>m; for(int a,b;m--;){ cin>>a>>b; add(a,b),add(b,a); } for(int i=1;i<=n;i++) if(!dfn[i])tarjan(i,0); cout<<cnt; }
#include<bits/stdc++.h> using namespace std; const int N=210,M=10010; int n,m,a,b; int h[N],to[M],ne[M],idx=1; //从2,3开始配对 void add(int a,int b){ to[++idx]=b;ne[idx]=h[a];h[a]=idx; } int dfn[N],low[N],tim,cnt; struct bridge{ int x,y; bool operator<(const bridge &t)const{ if(x==t.x) return y<t.y; return x<t.x; } }bri[M]; //割边 void tarjan(int x,int ine){ dfn[x]=low[x]=++tim; for(int i=h[x];i;i=ne[i]){ int y=to[i]; if(!dfn[y]){ //若y未访问 tarjan(y,i); low[x]=min(low[x],low[y]); if(low[y]>dfn[x]) bri[cnt++]={x,y}; } else if(i!=(ine^1)) //若y已访问且不是反边 low[x]=min(low[x],dfn[y]); } } int main(){ cin>>n>>m; while(m--){ cin>>a>>b; add(a,b),add(b,a); } for(int i=1;i<=n;i++) if(!dfn[i])tarjan(i,0); sort(bri,bri+cnt); for(int i=0;i<cnt;i++) cout<<bri[i].x<<" "<<bri[i].y<<"\n"; }
浙公网安备 33010602011771号