算法思路:
这个算法思路不难理解,任何一个强连通分量,必定是对原图的深度优先搜索树的子树。
那么其实,我们只要确定每个强连通分量的子树的根,然后根据这些根从树的最低层开始,一个一个的拿出强连通分量即可。那么眼下的问题就只
剩下如何确定强连通分量的根和如何从最低层开始拿出强连通分量了。
那么如何确定强连通分量的根,在这里我们维护两个数组,一个是indx[1..n],一个是mlik[1..n],其中indx[i]表示顶点i开始访问时间,mlik[i]为与
顶点i邻接的顶点,未删除顶点j的mlik[j]和mlik[i]的最小值(mlik[i]初始化为indx[i])。这样,在一次深搜的回溯过程中,如果发现mlik[i]==indx[i]那么,
当前顶点就是一个强连通分量的根,为什么呢?因为如果它不是强连通分量的跟,那么它一定是属于另一个强连通分量,而且它的根是当前顶点的祖
宗,那么存在包含当前顶点的到其祖宗的回路,可知mlik[i]一定被更改为一个比indx[i]更小的值。
至于如何拿出强连通分量,这个其实很简单,如果当前节点为一个强连通分量的根,那么它的强连通分量一定是以该根为根节点的(剩下节点)子
树。在深度优先遍历的时候维护一个堆栈,每次访问一个新节点,就压入堆栈。现在知道如何拿出了强连通分量了吧?是的,因为这个强连通分量时最
先被压人堆栈的,那么当前节点以后压入堆栈的并且仍在堆栈中的节点都属于这个强连通分量。当然有人会问真的吗?假设在当前节点压入堆栈以后压
入并且还存在,同时它不属于该强连通分量,那么它一定属于另一个强连通分量,但当前节点是它的根的祖宗,那么这个强连通分量应该在此之前已经
被拿出。现在没有疑问了吧,那么算法介绍就完了。
题意:给定一个有向图并规定:每两个点之间一定有边,同时A指向B则B定不能指向A,反之亦然。 询问是否存在仅有三个点构成的环。
思路:
首先判断有向图中是否存在环马上有tarjan能够很好的解决。并且如果存在大于三个点以上的环的话肯定存在仅有三个点构成的环。 因为每两
个点之间都有边,并且只有一个给定的指向,画几个图便可以推导出这样的结论。
证明: 假设存在一个n元环,因为a->b有边,b->a必定没边,反之也成立 所以假设有环上三个相邻的点a-> b-> c,那么如果c->a间有边,就已经形
成了一个三元环,如果c->a没边,那么a->c肯定有边,这样就形成了一个n-1元环。。。。 所以只需证明n为4时一定有三元环即可,显然成立。 所
以知道在tarjan后或中判断是否存在大于等于三个点的强连通图。复杂度为边数即O(n*n)。
#include <iostream>
#include <string.h>
#include <stdio.h>
using namespace std;
const int N=2005;
int head[N],low[N];
int dfn[N],stack[N];
int ans[N];
bool temp[N];
char str[N][N];
int index,m,flag;
typedef struct
{
int to;
int next;
}Node;
Node map[N*N];
void add(int u,int v)
{
map[index].to=v;
map[index].next=head[u];
head[u]=index++;
}
void Tarbfs(int k,int lay,int &scc_num)
{
int t;
temp[k]=1;
low[k]=lay;
dfn[k]=lay;
stack[m++]=k;
for(int i=head[k];i!=-1;i=map[i].next)
{
t=map[i].to;
if(!dfn[t])
{
Tarbfs(t,++lay,scc_num);
if(flag) return;
low[k]=min(low[k],low[t]);
}
if(temp[t])
{
low[k]=min(low[k],dfn[t]);
}
}
if(dfn[k]==low[k])
{
++scc_num;
while(1)
{
t=stack[--m];
temp[t]=0;
ans[scc_num]++;
if(ans[scc_num]>=3)
{
flag=1;
return;
}
if(t==k) break;
}
}
if(flag) return;
}
int Tarjan(int n)
{
int scc_num=0,lay=1;
m=0;
memset(temp,0,sizeof(temp));
memset(low,0,sizeof(low));
for(int i=1;i<=n;i++)
{
if(!dfn[i])
Tarbfs(i,lay,scc_num);
if(flag) break;
}
return scc_num;
}
int main()
{
int i,j,n,t=1,T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
memset(dfn,0,sizeof(dfn));
memset(ans,0,sizeof(ans));
memset(head,-1,sizeof(head));
memset(stack,0,sizeof(stack));
index=flag=0;
for(i=1;i<=n;i++)
scanf("%s",str[i]+1);
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
if(str[i][j]=='1')
{
add(i,j);
}
}
}
printf("Case #%d: ",t++);
Tarjan(n);
if(flag) puts("Yes");
else puts("No");
}
return 0;
}