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;
}