强连通分量

有向图的强连通分量:在有向图G中,如果两个顶点vi,vj间(vi!=vj)有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通。如果有向图G的每两个顶点都强连通,称G是一个强连通图。非强连通图有向图的极大强连通子图,称为强连通分量。如图所示:

强连通分量_第1张图片

{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达,{5},{6}也分别为两个强连通分量。

Tarjan算法是基于对图深度优先搜索的算法,每个强连通分量为搜索树中的一棵子树。搜索时,把当前搜索树中未处理的节点加入一个堆栈,回溯时可以判断栈顶到栈中的节点是否为一个强连通分量。

定义DFN(u)为节点u搜索的次序编号(时间戳),Low(u)为u或u的子树能够追溯到的最早的栈中节点的次序号。由定义可以得出:

Low(u)=Min{ DFN(u), Low(v),(u,v)为树枝边,u为v的父节点 DFN(v),(u,v)为指向栈中节点的后向边(非横叉边)}, 当DFN(u)=Low(u)时,以u为根的搜索子树上所有节点是一个强连通分量。算法伪代码如下:

tarjan(u)
{
	DFN[u]=Low[u]=++Index                      // 为节点u设定次序编号和Low初值
	Stack.push(u)                              // 将节点u压入栈中
	for each (u, v) in E                       // 枚举每一条边
		if (v is not visted)               // 如果节点v未被访问过
			tarjan(v)                  // 继续向下找
			Low[u] = min(Low[u], Low[v])
		else if (v in S)                   // 如果节点v还在栈内
			Low[u] = min(Low[u], DFN[v])
	if (DFN[u] == Low[u])                      // 如果节点u是强连通分量的根
		repeat
			v = S.pop                  // 将v退栈,为该强连通分量中一个顶点
			print v
		until (u== v)
} 

题1:POJ 1236(Network of Schools),题目意思不懂的直接百度。强连通分量的裸题。已经在代码上做了提示:

#include<iostream>
#include<cstring>
#include<stack>
using namespace std;
const int N=5000; //最大顶点数 
const int M=100010; //最大边数 
#define min(a,b) (a)<(b)?(a):(b)
#define max(a,b) (a)>(b)?(a):(b)
#define CLR(arr,val) memset(arr,val,sizeof(arr)) 
stack<int> s;
int n,m,t,sum;  //n为顶点个数,m为边数,t为时间戳,sum为强连通分量个数 
int DFN[N];  //保存顶点i搜索的次序(时间戳)编号
int Low[N];  //保存顶点i或i的子树最早的次序编号
int flag[N];  //记录点是否被保存在堆栈中 
int belong[N]; //用来缩点的数组 
struct ArcNode{
    void Add(int u,int v)
    {   next[num]=Prior[u];
        data[num]=v;
        Prior[u]=num++;
    } 
    void Init()
    {   CLR(Prior,-1);
        num=0;
    }
    int Prior[N],next[M],data[M],num;
}A;
void Tarjan(int u) //从顶点u进行DFS 
{   DFN[u]=Low[u]=++t;
    s.push(u);
    flag[u]=1; //标记顶点u已经被访问
    for(int i=A.Prior[u];i!=-1;i=A.next[i]) 
    {   int v=A.data[i];
        if(DFN[v]==0) //顶点v没有被访问过 
        {   Tarjan(v);
            Low[u]=min(Low[u],Low[v]);
        }
        else if(flag[v]) Low[u]=min(Low[u],Low[v]);
    }
    if(DFN[u]==Low[u]) //顶点u是强连通分量的根 
    {   int temp=-1;
        sum++;
        while(temp!=u) //缩点 
        {   temp=s.top();
            s.pop();
            belong[temp]=sum;
            flag[temp]=0; 
        }   
    }
}
void Slove()
{   CLR(DFN,0);
    for(int i=1;i<=n;i++)
        if(DFN[i]==0) Tarjan(i);
} 
void Count() //统计出度为0和入度为0的强连通分量的个数 
{   int Inum=0,Onum=0;
    int In[N],Out[N];
    CLR(In,0);
    CLR(Out,0);
    for(int i=1;i<=n;i++)
        for(int j=A.Prior[i];j!=-1;j=A.next[j]) 
            if(belong[i]!=belong[A.data[j]]) //如果顶点i和顶点A.data[j]不在同一个强连通分量中
                Out[belong[i]]=1,In[belong[A.data[j]]]=1;
    for(int i=1;i<=sum;i++)
    {   if(!In[i]) Inum++;
        if(!Out[i]) Onum++;
    } 
    cout<<Inum<<endl; //最少需要选择多少个顶点,使得从这些点出发能遍历完整个图            
    if(sum==1) cout<<0<<endl; //最少需要添加多少条有向边,使得整个图为强连通图 
    else cout<<(max(Inum,Onum))<<endl;
}
int main()
{   cin>>n;
    sum=0;
    t=0;
    A.Init();
    while(!s.empty()) s.pop(); 
    for(int i=1;i<=n;i++)
    {   int u; 
        while(cin>>u,u)
            A.Add(i,u);
    }  
    Slove();
    Count();
    return 0;
}

然后拿着这个代码在main里面添加个测试组数去把NYOJ 120(校园网络)过了吧。

你可能感兴趣的:(算法,struct,百度,测试,NetWork,each)