题目
成为牛群中最受欢迎的牛是每一头奶牛的梦想。在一个有N(1<=N<=10,100)只奶牛的牛群中,你将会得知M(1<=M<=50,000)对以(A,B)形式给出的数对,表示奶牛A认为奶牛B受欢迎。因为受欢迎是可传递的,所以如果奶牛A认为奶牛B受欢迎且奶牛B认为奶牛C受欢迎,那么即使在读入中没有出现数对(A,C),奶牛A也认为奶牛C受欢迎。你的任务是输出被所有奶牛欢迎的奶牛数量。
http://poj.org/problem?id=2186
题解
在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components)。
下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达。{5},{6}也分别是两个强连通分量。
直接根据定义,用双向遍历取交集的方法求强连通分量,时间复杂度为O(N^2+M)。更好的方法是Kosaraju算法或Tarjan算法,两者的时间复杂度都是O(N+M)。本文介绍的是Tarjan算法。
[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为根的搜索子树上所有节点是一个强连通分量。
以上内容转自Byvoid https://www.byvoid.com/zhs/blog/scc-tarjan
下面提供Pascal版本的tarjan:
procedure tarjan(i:longint);
var
j,t:longint;
begin
inc(Dindex);
dfn[i]:=Dindex;
low[i]:=Dindex;
inc(tot);
Stack[tot]:=i;
t:=ls[i];
while t>0 do
begin
j:=g[t].y;
if dfn[j]=0 then
begin
tarjan(j);
if low[i]>low[j] then
low[i]:=low[j];
end
else if instack[j] and (dfn[j]<low[i])
low[i]:=dfn[j];
end;
if dfn[i]=low[i] then
begin
inc(bcnt);
repeat
j:=Stack[tot];
dec(tot);
instack[j]:=false;
Belong[j]:=bcnt;
until i=j;
end;
end;
procedure solve;
var
i:longint;
begin
tot:=0;
bcnt:=0;
dindex:=0;
fillchar(dfn,sizeof(dfn),0);
for i:=1 to n do
if dfn[i]=0 then
tarjan(i);
end;
本题题解
先用tarjan求出图中的强连通分量,然后统计出度为0的强连通分量数目,如果唯一则这里面点的个数就是最后的答案。但是如果出度为0的强连通分量的个数大于1.那么则无解。
代码
#include <cstdio>
#include <cstring>
using namespace std;
const int ce=10005;
int n,m,max,c,de=0,tot,sum=0;
int ne[ce*5],y[ce*5],ls[ce],dfn[ce],low[ce],d[ce],stack[ce];
bool b[ce],bz[ce];
void tarjan(int i)
{
de++;
dfn[i]=de;
low[i]=de;
tot++;
b[i]=1;
stack[tot]=i;
int t=ls[i];
while (t>0)
{
int j=y[t];
if (dfn[j]==0)
{
tarjan(j);
if (low[j]<low[i])
low[i]=low[j];
} else
if (dfn[j]<low[i])
low[i]=dfn[j];
t=ne[t];
}
if (dfn[i]==low[i])
{
int e=0,w=0;
sum++;
while (stack[tot]!=i)
{
int j=stack[tot];
tot--;
d[j]=sum;
w++;
int t=ls[j];
while (t>0)
{
int g=y[t];
if (!b[g]) {e=1;break;}
t=ne[t];
}
}
int j=stack[tot];
tot--;
b[j]=0;
d[j]=sum;
w++;
int t=ls[j];
while (t>0)
{
int g=y[t];
if (!b[g]) {e=1;break;}
t=ne[t];
}
if (e==0) {c++;max=w;}
}
if (c>1) return;
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
{
int a,b;
scanf("%d%d",&a,&b);
ne[i]=ls[a];ls[a]=i;y[i]=b;
}
memset(b,0,sizeof(b));
for (int i=1;i<=n;i++)
if (dfn[i]==0)
tarjan(i);
if (c==1) printf("%d",max); else printf("0");
}