有向图的强连通分量(strongly connected components)
在有向图G中,如果两个顶点vi,vj间(vi!=vj)有一条从vi到vj的路径,同时还有一条从vj到vi的路径(顶点相互可达),则称两个顶点强连通。如果有向图G的每对顶点都强连通,称G是个强连通图。非强连通图有向图的极大强连通子图,称为强连通分量。
求解强连通分量的算法主要有三种:Kosaraju,Tarjan,Gabow。下面介绍Tarjan算法。
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算法的详细介绍,参考:
http://hi.baidu.com/escorter2009/blog/item/f35951dc5bdb3de677c63826.html
POJ2186
题目大意:有n头牛,每头牛都希望自己受欢迎,输入a b表示a认为b受欢迎。要求出受其它所有牛欢迎的牛的个数。
解:首先求强连通分量,分量中每头牛都受分量中其它牛的欢迎。分量之间至多有一条有向边。那么显然需要找出出度为0的强连通分量,若有大于1的出度为0的强连通分量,这两个连通分量互相不认为对方受欢迎,显然个数为0;则有且仅有一个入读度为0的强连通分量时,该强连通分量的结点数即为解。
//出度为0的scc
#include <iostream>
const int MAX = 10002;
int n,m;
int low[MAX],dfn[MAX],seq;
bool inStack[MAX];
struct Edge
{
int to;
int next;
}e[50001];
int index[MAX],edgeNum;
int stack[MAX],top;
int belong[MAX]; //belong[i]表示结点i属于的连通分量
int outdegree[MAX];
int cnt; //cnt为当前强连通分量的标记值
int min(int x, int y)
{
return x < y ? x : y;
}
void addEdge(int from, int to)
{
e[edgeNum].to = to;
e[edgeNum].next = index[from];
index[from] = edgeNum++;
}
void Tarjan(int u)
{
low[u] = dfn[u] = seq++;
stack[top++] = u; //将结点u入栈
inStack[u] = true;
for(int i = index[u]; i != -1; i = e[i].next)
{
int w = e[i].to;
if(dfn[w] < 0)
{
Tarjan(w);
low[u] = min(low[u],low[w]);
}
else if(inStack[w]) //如果节点w还在栈内
low[u] = min(low[u],dfn[w]);
}
if(dfn[u] == low[u]) //u是强连通分量的根
{
int v;
cnt++;
//出栈,缩点
do
{
top--;
v = stack[top];
inStack[v] = false;
belong[v] = cnt;
}while(u != v);
}
}
void solve()
{
int i,j;
for(i = 1; i <= n; i++)
if(dfn[i] < 0)
Tarjan(i);
for(i = 1; i <= n; i++)
{
for(j = index[i]; j != -1; j = e[j].next)
{
if(belong[i] != belong[e[j].to])
outdegree[belong[i]]++;
}
}
int num = 0; //出度为0的连通分量的个数
int who; //哪个连通分量
for(i = 1; i <= cnt; i++)
{
if(outdegree[i] == 0)
{
who = i;
num++;
}
}
if(num != 1)
printf("0\n");
else
{
int result = 0;
for(i = 1; i <= n; i++)
if(belong[i]==who)
result++;
printf("%d\n",result);
}
}
int main()
{
int i,j;
int from,to;
edgeNum = 0;
seq = 0;
top = 0;
cnt = 0;
memset(dfn,-1,sizeof(dfn));
memset(index,-1,sizeof(index));
memset(inStack,0,sizeof(inStack));
memset(outdegree,0,sizeof(outdegree));
scanf("%d %d",&n,&m);
for(i = 0; i < m; i++)
{
scanf("%d %d",&from,&to);
addEdge(from,to);
}
solve();
return 0;
}
POJ2553
题目大意:一个图由结点集v和边集E组成,现在要求出一个点集合:
bottom(G)={v∈V|∀w∈V:(v→w)⇒(w→v)}(对于集合中的任意一点v和V中的任意一点w,如果v能到达w,那么w也能到达v)。
解:首先求强连通分量,联想到出度为0的scc(因为出度不为0的scc,不满足集合的定义)。
//出度为0的所有scc
#include <iostream>
const int MAX = 5005;
int n,m;
struct Edge
{
int to;
int next;
}e[MAX*MAX];
int index[MAX],edgeNum;
int low[MAX],dfn[MAX],seq;
int stack[MAX],top;
bool inStack[MAX];
int belong[MAX],outdegree[MAX],cnt;
int result[MAX],count;
int min(int x, int y)
{
return x < y ? x : y;
}
void addEdge(int from, int to)
{
e[edgeNum].to = to;
e[edgeNum].next = index[from];
index[from] = edgeNum++;
}
void Tarjan(int u)
{
low[u] = dfn[u] = seq++;
stack[top++] = u;
inStack[u] = true;
for(int i = index[u]; i != -1; i = e[i].next)
{
int w = e[i].to;
if(dfn[w] < 0)
{
Tarjan(w);
low[u] = min(low[u],low[w]);
}
else if(inStack[w]) //如果节点w还在栈内
low[u] = min(low[u],dfn[w]);
}
if(low[u] == dfn[u])
{
int v;
cnt++;
do
{
top--;
v = stack[top];
inStack[v] = false;
belong[v] = cnt;
}while(u != v);
}
}
void solve()
{
int i,j;
for(i = 1; i <= n; i++)
if(dfn[i] < 0)
Tarjan(i);
for(i = 1; i <= n; i++)
{
for(j = index[i]; j != -1; j = e[j].next)
{
if(belong[i] != belong[e[j].to])
outdegree[belong[i]]++;
}
}
for(i = 1; i <= n; i++)
{
if(outdegree[belong[i]] == 0)
result[count++] = i;
}
}
int main()
{
int i,j;
int from,to;
while(true)
{
scanf("%d",&n);
if(n==0)
break;
edgeNum = top = seq = count = cnt =0;
memset(dfn,-1,sizeof(dfn));
memset(index,-1,sizeof(index));
memset(inStack,0,sizeof(inStack));
memset(belong,0,sizeof(belong));
memset(outdegree,0,sizeof(outdegree));
scanf("%d",&m);
for(i = 0; i < m; i++)
{
scanf("%d %d",&from,&to);
addEdge(from,to);
}
solve();
if(count == 0)
printf("\n");
else
{
for(i = 0; i < count-1; i++)
printf("%d ",result[i]);
printf("%d\n",result[i]);
}
}
return 0;
}