/先通过一道题目:
存在n个通讯站,它们之间可以相互通讯(直接或间接),若其中的一个通讯站损坏,那么是否会影响正常工作?
可以将这些通讯站分成几个子站,使得每个子站内部只损坏一个通讯站时还可以正常工作?
题目抽象:
通讯站以及他们之间的直接联系为无向图,通讯站为顶点,联系为边,则第一问求的是:去掉一个点以及它所连的边后,该无向图的连通性会被破坏,求这样的点集(也就是割顶)。
第二问:子站中去掉任意一个点后,不会影响该子站的联通性,即求的是该无向图的块(双连通分量)
算法求割顶以及块:
两者可以一同求出;
首先先明确前向边与后向边:当一个图进行DFS遍历时,一边遍历,会一边标记已经经过的点和边;当从点U扩展至点V,经过边(U,V);发现V已经被访问过,那么边(U,V)为后向边,相反,其他的边成为前向边。
再则,为每个点定义两个量:dfn以及low;dfn:表示图在DFS的过程中点的访问顺序;low:表示该点以及其子节点最前可以联系至的点(也就是可以联系至的最小的dfs值)
对于每个点,如果存在一个子节点,子节点的low>=该点的dfs,那么这个点就是该子节点的割点;
换句话说,如果一个节点,它的low>=它的父节点的dfs。那么这个节点的父节点就是割点,这个节点直到它上一个块为止遍历的所有节点再加上节点的父节点都是一个新的块。
具体算法步骤:
1.初始化一个栈,从某个节点开始DFS,->U,i=1。
2.将U的dfn设为i,i++;low=dfn,U入栈;
3.判断U是否可扩展;可扩展则转向4,如果不可扩展,转至6;
4.扩展至V,如果v被标记过,那么说明(U,V)边为后向边,low(U)=min(low(U),low(V)),回到步骤3;
5.如果没有被标记过,那么(U,V)为前向边,father(V)=U,U=V,转至步骤2;
6,判断father(U)是否为1,如果为1,则转至8;
7.判断low(U)<dfn(father(U)),是,low(father(U))=min(low(father(U)),low(U));不是,则father(U)为割点,且从U向上的栈中的所有节点加上father(U)组成一个块,将U及以上的点都出栈;U=father(U),转至3。
8.将U及以上的点出栈,加上初始点,组成一个块;
9.判断初始点是否还可以扩展,如果不可以,则结束;可以,则初始点也为一个割点,转至2;
代码:(没有严格验证,可能不够准确)
#include<iostream>
using namespace std;
bool ver[100][100]; //存储边
bool visp[100]; //点的访问情况
bool visv[100][100]; //边的访问情况
struct node{
int dfn;
int low;
};
node a[100]; //图中点在dfs时的访问顺序以及最大可达值
int father[100]; //dfs时点的父节点
int times; //点的访问值的记录
int top; //栈顶指针
int stack[100]; //栈
int b[100][100]; //存储得到的块的点
int point[100]; //存储得到的割顶
int pointnum; //割顶的数量
int ressquare; //得到的块的数量
int n,m;
int testfirst; //dfs中初始顶点可访问的关联边
int startpoint; //图dfs的开始顶点
int minn(int a1,int b1){
if(a1<b1)
return a1;
return b1;
}
void toquare(int st,int pd){ //由割顶得到块
int temp=0;
for(;stack[top]!=st;top--){
b[ressquare][temp]=stack[top-1];
temp++;
// cout<<stack[top-1]<<" ";
}
b[ressquare][temp++]=father[st];
if(pd==0){
point[pointnum++]=father[st];
// cout<<"point is "<<father[st]<<endl;
}
if(temp>=3){
ressquare++;
}
// cout<<father[st]<<endl;
return ;
}
int dfs(int start){
// cout<<start<<endl;
a[start].dfn=times;
a[start].low=times;
times++;
stack[top]=start;
top++;
visp[start]=true;
for(int j=1;j<=n;j++){
if(ver[start][j]&&visv[start][j]==false){
visv[start][j]=true;
if(visp[j]){
a[start].low=minn(a[j].dfn,a[start].low);
}
else{
if(start==startpoint){
testfirst++;
}
father[j]=start;
// stack[top++]=start;
dfs(j);
}
}
}
// cout<<start<<" "<<father[start]<<" "<<a[start].low<<" "<<a[start].dfn<<endl;
if(father[father[start]]==0){
toquare(start,1);
}
else if(a[father[start]].dfn>a[start].low){
a[father[start]].low=minn(a[father[start]].low,a[start].low);
return 0;
}
else {
// cout<<start<<" "<<stack[top-1]<<endl;
toquare(start,0);
}
return 0;
}
int main(){
// int n,m;
while(cin>>n>>m>>startpoint){
times=1;
top=0;
ressquare=0;
pointnum=0;
// startpoint=3;
testfirst=0;
memset(a,0,sizeof(a));
memset(father,0,sizeof(father));
memset(ver,false,sizeof(ver));
memset(visp,false,sizeof(visp));
memset(visv,false,sizeof(visv));
memset(point,0,sizeof(point));
memset(b,0,sizeof(b));
for(int i=0;i<m;i++){
int temp1,temp2;
cin>>temp1>>temp2;
ver[temp1][temp2]=true;
ver[temp2][temp1]=true;
}
dfs(startpoint);
if(testfirst>1)
point[pointnum++]=startpoint;
cout<<ressquare<<endl;
for(int g=0;g<ressquare;g++){
for(int i=0;;i++){
if(b[g][i]==0)
break;
cout<<b[g][i]<<" ";
}
cout<<endl;
}
cout<<"points are "<<endl;
for(int j=0;j<pointnum;j++){
cout<<point[j]<<endl;
}
}
return 0;
}