时间限制: 1 Sec 内存限制: 32 MB
提交: 530 解决: 91
题目描述
假若在删去顶点v以及和v相关联的各边之后,将图的一个连通分量分割成两个或两个以上的连接分量,则称顶点v为该图的一个关节点。一个没有关节点的连通图称为重连通图。在重连通图上,任意一对顶点之间至少存在两条路径,则在删去某个顶点以及依附于该顶点的各边时也不会破坏图的连通性。
利用深度优先搜索可以求出图的关节点,并由此可以判断图是否是重连通的。
通过修改深度优先搜索遍历的算法便可以得到求关节点的算法,其算法描述如下:
在本题中,读入一个无向图的邻接矩阵(即数组表示),建立无向图并按照以上描述中的算法求出所有的关节点,并输出这些关节点。
输入
输入的第一行包含一个正整数n,表示图中共有n个顶点。其中n不超过50。
以后的n行中每行有n个用空格隔开的整数0或1,对于第i行的第j个整数,如果为1,则表示第i个顶点和第j个顶点有直接连接,0表示没有直接连接。当i和j相等的时候,保证对应的整数为0。
输入保证邻接矩阵为对称矩阵,即输入的图一定是无向图,且保证图中只有一个连通分量。
输出
第一行有一个整数x,即图中关节点的个数。
第二行输出x个整数,表示所有关节点的顶点编号,请按照编号从小到大的顺序输出。每个整数后输出一个空格,并请注意行尾输出换行。
样例输入
0 1 1 1
1 0 0 0
1 0 0 0
1 0 0 0
样例输出
1
0
提示
在本题中,需要掌握图的深度优先遍历的方法,并需要掌握通过深度优先搜索求得图中关节点的算法。通过生成深度优先生成树可以得出两类关节点的特性:
1. 若生成树的根有两棵或两棵以上的子树,则此根顶点必为关节点。
2. 若生成树中某个非叶子顶点v,其某棵子树的根和子树中的其他结点均没有指向v的祖先的回边,则说明v是关节点。
注意以上两点特性,就可以成功的通过深度优先搜索遍历的算法得出图中的关节点了。
代码实现
#include <cstdio>
#include <cstdlib>
#include <algorithm>
using namespace std;
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define OVERFLOW -1
typedef int Status; /* 函数结果状态代码,如OK等 */
typedef int InfoType;
typedef int VertexType; /* 节点类型 */
#define MAX_VERTEX_NUM 50
typedef enum{DG,DN,AG,AN}GraphKind; /* {有向图,有向网,无向图,无向网} */
typedef struct ArcNode
{
int adjvex; /* 该弧所指向的顶点的位置 */
struct ArcNode *nextarc; /* 指向下一条弧的指针 */
InfoType *info; /* 网的权值指针) */
}ArcNode; /* 表结点 */
typedef struct
{
VertexType data; /* 顶点信息 */
ArcNode *firstarc; /* 第一个表结点的地址,指向第一条依附该顶点的弧的指针 */
}VNode,AdjList[MAX_VERTEX_NUM]; /* 头结点 */
typedef struct
{
AdjList vertices;
int vexnum,arcnum; /* 图的当前顶点数和弧数 */
int kind; /* 图的种类标志 */
}ALGraph;
int cnt; /* 全局量cnt对访问计数 */
int low[MAX_VERTEX_NUM];
int articulcnt;
int articul[MAX_VERTEX_NUM];
bool visited[MAX_VERTEX_NUM]; /* 访问标志数组(全局量) */
//void(*VisitFunc)(char* v); /* 函数变量(全局量) */
Status CreateGraph(ALGraph *G)
{ /* 采用邻接表存储结构,构造没有相关信息的图G(用一个函数构造4种图) */
int i,j,k;
int temp; /* 临时变量 */
VertexType va,vb;
ArcNode *p;
(*G).kind = 2;
scanf("%d", &(*G).vexnum);
(*G).arcnum = 0;
for(i=0;i<(*G).vexnum;i++)
{
(*G).vertices[i].data=i;
(*G).vertices[i].firstarc=NULL;
}
for(i=0;i<(*G).vexnum;i++)/* 构造表结点链表 */
{
for(j=0;j<(*G).vexnum;j++)
{
scanf("%d",&temp);
if(i<j&&temp==1)
{
va=i;/* 弧尾 */
vb=j;/* 弧头 */
p=(ArcNode*)malloc(sizeof(ArcNode));
p->adjvex=j;
p->info=NULL; /* 图 */
p->nextarc=(*G).vertices[i].firstarc; /* 插在表头 */
(*G).vertices[i].firstarc=p;
p=(ArcNode*)malloc(sizeof(ArcNode));
p->adjvex=i;
p->info=NULL; /* 无向图 */
p->nextarc=(*G).vertices[j].firstarc; /* 插在表头 */
(*G).vertices[j].firstarc=p;
}
}
}
return OK;
}
void DFSArticul(ALGraph G,int v0)
{ /* 从第v0个顶点出发深度优先遍历图G,查找并输出关节点。算法7.11 */
int min,w;
ArcNode *p;
visited[v0]=min=++cnt; /* v0是第cnt个访问的顶点 */
for(p=G.vertices[v0].firstarc;p;p=p->nextarc) /* 对v0的每个邻接顶点检查 */
{
w=p->adjvex; /* w为v0的邻接顶点 */
if(visited[w]==0) /* w未曾访问,是v0的孩子 */
{
DFSArticul(G,w); /* 返回前求得low[w] */
if(low[w]<min)
min=low[w];
if(low[w]>=visited[v0])
articul[articulcnt++]=G.vertices[v0].data; /* 关节点 */
}
else if(visited[w]<min)
min=visited[w]; /* w已访问,w是v0在生成树上的祖先 */
}
low[v0]=min;
}
void FindArticul(ALGraph G)
{ /* 连通图G以邻接表作存储结构,查找并输出G上全部关节点。算法7.10 */
/* 全局量cnt对访问计数。 */
int i,v;
ArcNode *p;
cnt=1;
low[0]=visited[0]=1; /* 设定邻接表上0号顶点为生成树的根 */
for(i=1;i<G.vexnum;++i)
visited[i]=0; /* 其余顶点尚未访问 */
p=G.vertices[0].firstarc;
v=p->adjvex;
DFSArticul(G,v); /* 从第v顶点出发深度优先查找关节点 */
if(cnt<G.vexnum) /* 生成树的根有至少两棵子树 */
{
articul[articulcnt++]=G.vertices[0].data; /* 根是关节点,输出 */
while(p->nextarc)
{
p=p->nextarc;
v=p->adjvex;
if(visited[v]==0)
DFSArticul(G,v);
}
}
}
int main()
{
ALGraph g;
articulcnt=0;
CreateGraph(&g);
FindArticul(g);
sort(articul,articul+articulcnt);
articulcnt=unique(articul,articul+articulcnt)-articul;
printf("%d\n",articulcnt);
for(int i=0;i<articulcnt;i++)
{
printf("%d ", articul[i]);
}
puts("");
}