割顶和桥:对于无向图G,如果删除某个节点u后,连通分量数目增加,则称u为图的割顶;如果删除某条边后,连通分量数目增加,则称该边为图的桥。对于连通图删除割顶或桥后都会使得图不再连通
求图中割顶和桥的代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn =1000;
int n,m;
vector<int> G[maxn];
int low[maxn],dfn[maxn];
int dfs_clock;
int iscut[maxn];
int dfs(int u,int fa)
{
int lowu = dfn[u] = ++dfs_clock;
int child = 0;
for(int i=0;i<G[u].size();i++){
int v = G[u][i];
if(!dfn[v]){
child++;
int lowv = dfs(v,u);
lowu = min(lowu,lowv);
if(lowv >= dfn[u]){
iscut[u] = 1;
}
if(lowv > dfn[u]){
cout<<"桥:"<<u<<"-"<<v<<endl;
}
}else if(dfn[v] < dfn[u] && v != fa){//用反向边更新lowu
lowu = min(lowu,dfn[v]);
}
}
if(fa < 0 && child == 1) iscut[u] = 0; //对于根节点的处理
low[u] = lowu;
return lowu;
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF){
memset(dfn,0,sizeof(dfn));
memset(iscut,0,sizeof(iscut));
for(int i=0;i<=n;i++){
G[i].clear();
}
int u,v;
for(int i=0;i<m;i++){
scanf("%d%d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
dfs(1,-1);
for(int i=1;i<=n;i++){
if(iscut[i]){
cout<<i<<endl;
}
}
}
return 0;
}
点_双连通分量 BCC:
对于一个连通图,如果任意两点至少存在两条“点不重复”的路径,则说图是点双连通的(即任意两条边都在一个简单环中),点双连通的极大子图称为点_双连通分量。
易知每条边属于一个连通分量,且连通分量之间最多有一个公共点,且一定是割顶.
代码讲解:在理解了上面找割顶的代码后,以上求BCC的代码就是用一个栈保存所有的访问的边,然后在找到一个割顶之后就将该割顶信息全部出栈后保存起来即可。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1000;
struct edge
{
int v,next;
};
edge edges[maxn];
int head[maxn],e;
struct Edge
{
int u,v;
Edge(int uu,int vv){
u=uu;
v=vv;
}
};
stack<Edge> s;
int n,m;
int dfn[maxn];
int dfs_clock;
int iscut[maxn];
int bcc_cnt;
int bccno[maxn];
vector<int>bcc[maxn];
void init()
{
memset(dfn,0,sizeof(dfn));
memset(iscut,0,sizeof(iscut));
memset(head,-1,sizeof(head));
memset(bccno,0,sizeof(bccno));
e = 0; dfs_clock = 0; bcc_cnt = 0;
}
void add_edges(int u,int v)
{
edges[e].v = v;
edges[e].next = head[u];
head[u] = e++;
edges[e].v = u;
edges[e].next = head[v];
head[v] = e++;
}
int dfs(int u,int fa)
{
int lowu = dfn[u] = ++dfs_clock;
int child = 0;
for(int i=head[u];i!=-1;i=edges[i].next){
int v = edges[i].v;
Edge e = (Edge){u,v};
if(!dfn[v]){
s.push(e);
child++;
int lowv = dfs(v,u);
lowu = min(lowu,lowv);
if(lowv >= dfn[u]){
iscut[u] = 1;
bcc_cnt++;
bcc[bcc_cnt].clear();
for(;;){
Edge x = s.top(); s.pop();
if(bccno[x.u] != bcc_cnt){
bcc[bcc_cnt].push_back(x.u);
bccno[x.u] = bcc_cnt;
}
if(bccno[x.v] != bcc_cnt){
bcc[bcc_cnt].push_back(x.v);
bccno[x.v] = bcc_cnt;
}
if(x.u == u && x.v == v){
break;
}
}
}
}else if(dfn[v]<dfn[u] && v != fa){
s.push(e);
lowu = min(lowu,dfn[v]);
}
}
if(fa < 0 && child == 1){
iscut[u] = 0;
}
return lowu;
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF){
init();
int u,v;
for(int i=0;i<m;i++){
scanf("%d%d",&u,&v);
add_edges(u,v);
}
dfs(1,-1);
for(int i=1;i<=bcc_cnt;i++){
if(bcc[i].size()>2){
for(int j=0;j<bcc[i].size();j++){
cout<<bcc[i][j]<<" ";
}
cout<<endl;
}
}
}
return 0;
}
边_双连通分量 EBC:
对于边_双连通分量的求解简单多了,先找出所有的桥,并将其做上标记。然后在利用dfs遍历连通分量即可,只需在遍历时不能访问桥即可。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1000;
struct Edge
{
int no,v,next;
};
Edge edges[maxn];
int n,m;
int e,head[maxn];
int dfn[maxn];
int dfs_clock;
int isbridge[maxn];
int ebcnum;
vector<int> ebc[maxn];
void init()
{
memset(dfn,0,sizeof(dfn));
memset(isbridge,0,sizeof(isbridge));
memset(head,-1,sizeof(head));
e = 0; ebcnum = 0;
}
void addedges(int num,int u,int v)
{
edges[e].no = num;
edges[e].v = v;
edges[e].next = head[u];
head[u] = e++;
edges[e].no = num++;
edges[e].v = u;
edges[e].next = head[v];
head[v] = e++;
}
int dfs_findbridge(int u,int fa)
{
int lowu = dfn[u] = ++dfs_clock;
for(int i=head[u];i!=-1;i=edges[i].next){
int v = edges[i].v;
if(!dfn[v]){
int lowv = dfs_findbridge(v,u);
lowu = min(lowu,lowv);
if(lowv > dfn[u]){
isbridge[edges[i].no] = 1;
}
}else if(dfn[v] < dfn[u] && v != fa){
lowu = min(lowu,dfn[v]);
}
}
return lowu;
}
void dfs_coutbridge(int u,int fa)
{
ebc[ebcnum].push_back(u);
dfn[u] = ++dfs_clock;
for(int i=head[u];i!=-1;i=edges[i].next){
int v = edges[i].v;
if(!isbridge[edges[i].no]&&!dfn[v]){
dfs_coutbridge(v,u);
}
}
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF){
init();
int u,v;
for(int i=0;i<m;i++){
scanf("%d%d",&u,&v);
addedges(i,u,v);
}
dfs_findbridge(1,-1);
memset(dfn,0,sizeof(dfn));
for(int i=1;i<=n;i++){
if(!dfn[i]){
ebc[ebcnum].clear();
dfs_coutbridge(i,-1);
ebcnum++;
}
}
for(int i=0;i<ebcnum;i++){
if(ebc[i].size()>1){
for(int j=0;j<ebc[i].size();j++){
cout<<ebc[i][j]<<" ";
}
cout<<endl;
}
}
}
return 0;
}