当前位置: 代码迷 >> 综合 >> 求桥和割点的Tarjan算法
  详细解决方案

求桥和割点的Tarjan算法

热度:27   发布时间:2023-09-23 07:50:25.0

low[u]定义为u或者u的子树中能够通过非父子边追溯到的最早的节点的DFS开始时间

dfn[u]表示dfs下u的开始时间


割点:无向连通图中,如果删除某点后,图变成不连通,则称该点为割点。

桥:无向连通图中,如果删除某边后,图变成不连通,则称该边为桥。


判断割点方法:

(1) u为树根,且u有多于一个子树。
(2) u不为树根,且存在(u,v)为树枝边(或称父子边,即u为v在搜索树中的父亲),使得dfn(u)<=low(v)。
   也就是u的子树中的v点无法到达u之前的点,所以u点去掉就是两个连通分支,所以u为割点


判断桥的方法:

一条边(u,v)是桥,当且仅当(u,v)为树枝边(即非负边),且满足dfn(u)<low(v)(前提是其没有重边)。

也就是,u的儿子v之间只有一条边(前提是无重边),且v点只能到v点到不了v点前,所以(u,v)边去掉就是两个连通分支,所以(u,v)为桥

注意:找桥的时候,要注意看有没有重边。有重边,则不是桥。


以下为代码:

割点和桥保存在cutvex和bridge中

1)在Tarjan算法的时候直接找桥割点:

#include<iostream>
#include<cstdio>
#include<vector>
#include<stack> 
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=10000+10;
typedef pair<int,int> Edge; 
int index=0,rootson=0;  
vector<bool> vis(maxn);    
vector<int> dfn(maxn),low(maxn),cutvex;
vector<vector<int> > G(maxn);  
vector<Edge> bridge;
void Tarjan(int u,int father)
{dfn[u]=low[u]=++index;vis[u]=true;bool isCut=false;for(int i=0;i<G[u].size();i++){int v=G[u][i];if(!vis[v]){Tarjan(v,u);low[u]=min(low[u],low[v]);if(dfn[u]<low[v])  bridge.push_back(Edge(u,v));if(dfn[u]<=low[v]) isCut=true;}else if(father!=v) //只能由非父子边更新 low[u]=min(low[u],dfn[v]);}if(father==1&&rootson!=-1) rootson++;if(rootson>1) cutvex.push_back(1),rootson=-1; if(u!=1&&isCut)	cutvex.push_back(u);
}
int main()
{int n,m;cin>>n>>m;while(m--){int u,v;cin>>u>>v;G[u].push_back(v);G[v].push_back(u);}Tarjan(1,0);  //联通的sort(cutvex.begin(),cutvex.end());sort(bridge.begin(),bridge.end());for(int i=0;i<cutvex.size();i++)cout<<cutvex[i]<<endl;for(int i=0;i<bridge.size();i++)cout<<bridge[i].first<<','<<bridge[i].second<<endl;return 0;
}




2)先把数据保存下来,再找桥割点:

#include<iostream>
#include<cstdio>
#include<vector>
#include<stack> 
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=10000+10;
typedef pair<int,int> Edge; 
int index=0;  
vector<bool> vis(maxn);    
vector<int> dfn(maxn),low(maxn),Father(maxn),cutvex;
vector<vector<int> > G(maxn);  
vector<Edge> bridge;
void Tarjan(int u,int father)
{dfn[u]=low[u]=++index;Father[u]=father;vis[u]=true;for(int i=0;i<G[u].size();i++){int v=G[u][i];if(!vis[v]){Tarjan(v,u);low[u]=min(low[u],low[v]);}else if(father!=v) //只能由非父子边更新 low[u]=min(low[u],dfn[v]);}
}
int Count(int n)
{int nson=0; //根节点的子树Tarjan(1,0);for(int v=2;v<=n;v++) //1为根 {int u=Father[v];if(u==1) nson++;else if(dfn[u]<=low[v]) cutvex.push_back(u);}       //v到不了u之前的点if(nson>1) cutvex.push_back(1);sort(cutvex.begin(),cutvex.end());for(int u=1;u<=n;u++){int v=Father[u];if(v>0&&dfn[v]<low[u]) bridge.push_back(Edge(v,u));} 
}
int main()
{int n,m;cin>>n>>m;while(m--){int u,v;cin>>u>>v;G[u].push_back(v);G[v].push_back(u);}Tarjan(1,0);  //联通的Count(n);for(int i=0;i<cutvex.size();i++)cout<<cutvex[i]<<endl;for(int i=0;i<bridge.size();i++)cout<<bridge[i].first<<','<<bridge[i].second<<endl;return 0;
} 
int main()
{int n,m;cin>>n>>m;while(m--){int u,v;cin>>u>>v;G[u].push_back(v);G[v].push_back(u);}Tarjan(1,0);  //联通的Count(n);for(int i=0;i<cutvex.size();i++)cout<<cutvex[i]<<endl;for(int i=0;i<bridge.size();i++)cout<<bridge[i].first<<','<<bridge[i].second<<endl;return 0;
}