POJ 1523 / ZOJ 1119 - Tarjan算法

来源:互联网 发布:创维网络电视机顶盒 编辑:程序博客网 时间:2024/05/17 04:47
题意:
给出各对存在边的点(序号范围0~1000),求割点及去掉该点后连通分量个数。
输出割点编号及连通分量个数。
分析:
裸的Tarjan算法解决这道题。
在这里需要在dfs函数中解决这几个问题:
(1)如何判断v是u的祖先结点;
(2)如何判断v是u的儿子结点;
(3)如何判断(v,u)是回边。
从图中的某个顶点u出发进行dfs时,要判断其他每个顶点v是否和u邻接——根据邻接矩阵存储的0和1得出;判断v是否访问——用数组visited[v]记录;
若u,v邻接,且此时v还未访问过,则v是u的儿子结点;
若此时v已访问过,则v是u的祖先结点,且(u,v)是一条回边;
每次从u的某个邻接顶点回退到u时,会计算low[u];当u的所有邻接顶点都访问完毕,low[u]才计算完毕。因为访问完所有邻接顶点才能将low[u]确定下来。
#include<iostream>#include<cstdio>#include<cstring>using namespace std;int Edge[1005][1005];//邻接矩阵int visited[1005],dfn[1005],low[1005],nodes,tmpdfn;//low:判断当前点是否割点;tmpdfn当前深度优先数int son;//根结点的儿子数(大于2时 根结点是割点)int subnets[1005];//去掉该结点后的连通分量个数int min(int a,int b){return (a>b?b:a);}void init(){low[1]=dfn[1]=1;tmpdfn=1;son=0;memset(visited,0,sizeof(visited));memset(subnets,0,sizeof(subnets));visited[1]=1;}void dfs(int u){//深搜,记录low[u],根据low[u]判断是否求割点int v;for(v=1;v<=nodes;v++){if(Edge[u][v]){//u,v间有边if(!visited[v]){//v is u's sonvisited[v]=1;tmpdfn++;dfn[v]=low[v]=tmpdfn;dfs(v);//dfs(v)执行完后,low[v]值已求出low[u]=min(low[u],low[v]);//回退时计算low[u]if(low[v]>=dfn[u]){if(u!=1) subnets[u]++;else son++;}}else low[u]=min(low[u],dfn[v]);}}}int main(){int t=1,i,u,v;//输入的顶点对int find;//是否找到SPF节点——割点的标志while(1){scanf("%d",&u);if(u==0) break;scanf("%d",&v);memset(Edge,0,sizeof(Edge));nodes=u;if(v>nodes) nodes=v;Edge[u][v]=Edge[v][u]=1;//printf("%d %d\n",u,v);while(1){scanf("%d",&u);if(u==0) break;scanf("%d",&v);if(u>nodes) nodes=u;if(v>nodes) nodes=v;Edge[u][v]=Edge[v][u]=1;//printf("%d %d\n",u,v);}//至此读完当前组的所有顶点对数据/*for(i=1;i<6;i++){for(int j=1;j<6;j++)printf("Edge[%d][%d]:%d  ",i,j,Edge[i][j]);puts("");}*/if(t>1) puts("");//确保最后一行输出完后不再输出空行printf("Network #%d\n",t++);//printf("%d %d\n",u,v);init();dfs(1);//Tarjan算法:只需要从某个顶点出发进行一次遍历即可if(son>1)subnets[1]=son-1;find=0;for(i=1;i<=nodes;i++){if(subnets[i]){find=1;printf("  SPF node %d leaves %d subnets\n",i,subnets[i]+1);}}if(!find)puts("  No SPF nodes");}return 0;}


0 0
原创粉丝点击