题目链接: hdu 1269
存在a到b的路径和b到a的路径
解题思路: 判断是否仅有一个强联通分量
Kosaraju算法的核心思想:
从未访问过的点用Kosaraju正向边搜索遍历完所有的顶点,记录层次dnf[ ]
然后按照深度优先搜索层次从小到大顺序再搜索一次反向边
若是图G‘强联通分量,无论正向边还是反向边搜索任意两个顶点能互相到达
若不是,则正向搜索无法到达或者逆向搜索无法到达
Kosaraju算法的执行过程:
1.对原图G进行深度优先搜索,并记录每个顶点的dnf值;
2.将图G的割边进行反向,得到其逆图GT;
3.选择从当前dnf值最小的顶点出发,对逆图GT进行DFS搜索,删除能够遍历到的顶点
这些顶点构成一个强联通分量;
4.如果还有顶点没有删除,继续执行第(3)步,否则算法结束.
代码:
//Final kosaraju判断有向图是否为强联通图
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX 10100
struct snode{
int to,next;
}edge1[MAX*30],edge2[MAX*30],edge3[MAX*30];
int visit1[MAX],In[MAX],To[MAX],pre1[MAX],pre2[MAX],Index1,Index2,k,list[MAX];
int father[MAX];
void Add_edge1(int a,int b) //建立正向图
{
edge1[Index1].to=b,edge1[Index1].next=pre1[a];
pre1[a]=Index1++;
}
void Add_edge2(int a,int b) //建立逆向图
{
edge2[Index2].to=b,edge2[Index2].next=pre2[a];
pre2[a]=Index2++;
}
void Kosaraju(int u) //第一次正向图搜索
{
int i,v;
for(i=pre1[u];i!=-1;i=edge1[i].next)
{
v=edge1[i].to;
if(visit1[v]==0)
{
visit1[v]=1;
Kosaraju(v);
}
}
list[k++]=u;
}
void DFS(int u,int Father) //第二次逆向图搜索
{
int i,v;
visit1[u]=2;
father[u]=Father;
for(i=pre2[u];i!=-1;i=edge2[i].next)
{
v=edge2[i].to;
if(visit1[v]==1)
DFS(v,Father);
}
}
int main()
{
int n,m,i,j,a,b,c,pd;
while(scanf("%d%d",&n,&m)!=EOF)
{
if(n==0&&m==0)
break;
Index1=Index2=0;
memset(In,0,sizeof(In));
memset(pre1,-1,sizeof(pre1));
memset(pre2,-1,sizeof(pre2));
memset(visit1,0,sizeof(visit1));
memset(father,0,sizeof(father));
for(i=0;i<m;i++)
{
scanf("%d%d",&a,&b);
Add_edge1(a,b);
Add_edge2(b,a);
}
for(i=1,k=0;i<=n;i++)
{
if(!visit1[i])
{
visit1[i]=1;
Kosaraju(i);
}
}
for(j=k-1,c=0;j>=0;j--) //第二次搜索和第一次搜索顶点的顺序一样
{
if(visit1[list[j]]==1)
{
DFS(list[j],++c);
}
}
for(i=1,pd=0;i<n;i++) //有两个联通块就说明不是联通图
if(father[i]!=father[i+1])
pd=1;
if(pd==0)
printf("Yes\n");
else
printf("No\n");
}
return 0;
}