/*
题目地址:http://poj.org/problem?id=1523
题意:给你一个关系网,求其中的关节点——去掉该点后一个连通的分块变成两个或
两个以上。
题中点的出现次序混乱,并且可能不连续,所以要为每个点建立索引,方便操作
题解:tarjan算法
参看:http://blog.csdn.net/wsniyufang/article/details/6604458
http://blog.csdn.net/wsniyufang/article/details/6611781
dfn[v]记录到达点v 的时间,low[v]表示通过它的子结点
可以到达的所有点中时间最小值,即low[i]=min(low[i],low[u]),u 为v 的
了孙,初始化时low[v]=dfn[u]。如果low[v]比dfn[v]小,说明v 可以通过
它的子结点u,u1,u2...到达它的祖先v',则存在环,这个环上所有的点组成的
子图便是一个强连通分量。换一个角度看,如果当low[v]==dfn[v]时,则它的
子树中所有low[u]==dfn[v]的点都与v 构成一个环,维护一个栈,DFS 过程中,
每遍历一个点则把它放入栈中,当发现low[v]==dfn[v]则依次把栈里的元素都
弹出来,当栈顶元素为v 时结束,这些点便构成一个以v 为树根的强连通分量。
分析:
如果low[u]>=dfn[v],说明v 的儿子u 不能通过其他边到达v 的祖先,此
时如果拿掉v,则必定把v 的祖先和v 的儿子u,及它的子孙分开,于是v 便是
一个割点,v 和它的子孙形成一个块。
如果low[u]>dfn[v]时,则说明u 不仅不能到达v 的祖先,连v 也不能通
过另外一条边直接到达,从而它们之间的边w(v,u)便是割边,
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<algorithm>
using namespace std;
const int MAXE=1000002;
const int MAXN=1002;
vector<int> node[MAXN];
int turn[MAXN]; //turn[i]表示实际点i对应的索引
int back[MAXN]; //back[i]表示索引i对应的实际点号
int num,tmpnum; //num点的数目
int dfn[MAXN]; // dfn[u]:节点u搜索的次序编号(时间戳)
int low[MAXN]; // low[u]:是u或u的子树能够追溯到的最早的栈中节点的次序号
int Stack[MAXN];
int in[MAXN]; //in[i]+1表示点i去掉后原先的块散落成的块数
int time;
int top; //栈顶指针
void Tarjan(int x,int fa)
{
dfn[x]=low[x]=time++;
Stack[++top]=x;
int count=0;
int size=node[x].size();
for(int i=0;i<size;i++)
{
int v=node[x][i];
if(v==fa)continue;
if(dfn[v]==0)
{
count++;
Tarjan(v,x);
low[x]=min(low[x],low[v]);
if(dfn[x]<=low[v])//若子节v点无法第二次连到x,且v无法连到x的祖先,则x必为割点
{
if(x!=fa)
in[x]++;
for(;;)
{
if(Stack[top]==x)break;
top--;
}
}
}
else
{
low[x]=min(low[x],dfn[v]);
}
}
if(fa==x)//根节点特判,如果根节点有两条dfs出口,既从根dfs一次后,根还有子节点未被访问,则次跟也是割点
{
in[x]=count-1;
}
}
void solve()
{
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(in,0,sizeof(in));
time=1;
top=-1;
for(int i=1;i<num;i++)
{
if(dfn[i]==0)
{
Tarjan(i,i);
}
}
int flag=0;
cout<<"Network #"<<tmpnum++<<endl;
for(int i=1;i<num;i++)
{
if(in[i]>0)
{
flag=1;
cout<<" SPF node "<<back[i]<<" leaves "<<in[i]+1<<" subnets"<<endl;
}
}
if(flag==0)
cout<<" No SPF nodes"<<endl;
cout<<endl;
}
int main()
{
int u,v,flag;
flag=0;
num=1;
tmpnum=1;
memset(turn,0,sizeof(turn));
for(int i=0;i<MAXN;i++)
node[i].clear();
for(;;)
{
scanf("%d",&u);if(u!=0)flag=1;
if(u==0)
{
if(flag)
{
solve();
flag=0;
num=1;
memset(turn,0,sizeof(turn));
memset(turn,0,sizeof(turn));
for(int i=0;i<MAXN;i++)
node[i].clear();
continue;
}
else return 0;
}
scanf("%d",&v);
if(turn[u]==0){turn[u]=num;back[num]=u;num++;}//为u建立索引
if(turn[v]==0){turn[v]=num;back[num]=v;num++;}
node[turn[u]].push_back(turn[v]);
node[turn[v]].push_back(turn[u]);
}
return 0;
}
POJ1523 SPF 求割点及连通块数 tarjin算法
最新推荐文章于 2020-02-06 15:27:37 发布