tarjan有向图求强连通分量
学前需知:
DFS,前向星,图论知识(强连通,反向边,横边,树边)
重点:
low的定义,tarjan过程模拟几遍
学习链接:
http://blog.miskcoo.com/2016/07/tarjan-algorithm-strongly-connected-components
例题:
poj2553
题意:
有向图n<=5000,找出所有的sink点,sink点是指sink点为起点经过的所有点都可以走回自己
思路:
scc后的DAG中出度为0就能确保这一点,
否则,sccA经过sccB,sccB中的点都无法走回sccA的点,与sink的定义不符
63ms
#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
const int maxn=20005;
const int maxe=50005;
int low[maxn];
int dfn[maxn];
bool ins[maxn];
int sk[maxn];
int poi=0,idx=0,cnt=0;
int num=0;
int head[maxn];
int to[maxe*2];
int nxt[maxe*2];
int in[maxn];//入度
int ou[maxn];//出度
int scc[maxn];
void add(int u,int v)
{
cnt++;
nxt[cnt]=head[u];
head[u]=cnt;
to[cnt]=v;
}
void tarjan(int u)
{
dfn[u]=low[u]=++idx;
ins[u]=1;
sk[poi++]=u;
for(int i=head[u]; i!=-1; i=nxt[i])
{
int v=to[i];
if(!dfn[v])
{
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(ins[v]) low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u])
{
num++;
int t;
do
{
t=sk[--poi];
scc[t]=num;
ins[t]=0;
}
while(t!=u);
}
}
int main()
{
int n,m;
while(~scanf("%d",&n))
{
if(n==0)break;
scanf("%d",&m);
int u,v;
memset(ins,0,sizeof(ins));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(head,-1,sizeof(head));
memset(ou,0,sizeof(ou));
memset(in,0,sizeof(in));
num=poi=cnt=idx=0;
for(int i=1; i<=m; i++)
{
scanf("%d %d",&u,&v);
add(u,v);
}
for(int i=1; i<=n; i++)
if(!dfn[i])tarjan(i);
for(int u=1; u<=n; u++)
{
for(int i=head[u]; i!=-1; i=nxt[i])
{
int v=to[i];
if(scc[u]!=scc[v])
{
in[scc[v]]++;
ou[scc[u]]++;
}
}
}
int ans1=0; int ans2=0;
for(int i=1; i<=n; i++)
{
if(ou[scc[i]]==0)printf("%d ",i);
}
printf("\n");
}
}
POJ2186
题意:
给有向图N<=10000,M<=50000,问有多少个点使得该点被其他所有点经过若干路径到达
思路:
原图缩点后,仅存在一个出度为0的点
(缩点后每个点都是强连通点集)的点集大小就是答案了
79ms
#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
const int maxn=20005;
const int maxe=50005;
int low[maxn];
int dfn[maxn];
bool ins[maxn];
int sk[maxn];
int poi=0,idx=0,cnt=0;
int num=0;
int head[maxn];
int to[maxe*2];
int nxt[maxe*2];
//int in[maxn];//入度
int ou[maxn];//出度
int scc[maxn];
int id[maxn];
void add(int u,int v)
{
cnt++;nxt[cnt]=head[u];
head[u]=cnt; to[cnt]=v;
}
void tarjan(int u)
{
dfn[u]=low[u]=++idx;
ins[u]=1;
sk[poi++]=u;
for(int i=head[u];i!=-1;i=nxt[i])
{
int v=to[i];
if(!dfn[v])
{
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(ins[v]) low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u])
{
num++;
int t;
do{
t=sk[--poi];
scc[t]=num;
ins[t]=0;
id[num]++;
}
while(t!=u);
}
}
int main()
{
int n,m;
while(~scanf("%d %d",&n,&m))
{
int u,v;
memset(ins,0,sizeof(ins));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(head,-1,sizeof(head));
memset(ou,0,sizeof(ou));
// memset(in,0,sizeof(in));
num=poi=cnt=idx=0;
for(int i=1;i<=m;i++)
{
scanf("%d %d",&u,&v);
add(u,v);
}
for(int i=1;i<=n;i++)
{
if(!dfn[i])
tarjan(i);
}
int ans=0;
for(int u=1;u<=n;u++)
{
for(int i=head[u];i!=-1;i=nxt[i])
{
int v=to[i];
if(scc[u]!=scc[v])
{
ou[scc[u]]++;
}
}
}
int flag=0;
for(int i=1;i<=num;i++)
{
if(ou[i]==0)
ans=i,flag++;
}
if(flag==1)
printf("%d\n",id[ans]);
else
printf("0\n");
}
}