题意:
给你一张有向图,问你这个图是否任意两点之间都能够相互到达
题解:
好像是Tarjan的模板题,已经不记得这个知识点了。况且一题多解的能力是必要的.
看到这道题第一反应就是首先在图中找到一棵有向树包含所有节点,且如果这个图是强联通的话是必然能实现的。然后呢?
在有向树上选择一个点x能够到达其它任意一个非同子树点y的必要条件就是x的子树上有一条边连到y和y的所有祖先节点中任意一个。
考虑y必然需要是根节点,同时x必然有叶子节点的情况,所以这道题变成了找到一棵有向树联通所有点,并且使叶子节点能够都连到根。
但是它本质是一张图,所以叶子节点之间也可能相互存在边,但是我们要做的事情还是看是否所有叶子能够连到根。多源点找单点是否都联通可能不太好找,但是找单源点能否访问其它点就只需要一个dfs即可。
所以我的思路是先构造一个树,然后建立反向边,如果根通过反向边能访问所有叶子,那么就是一个强连通图。
如何构造一棵树?由于确定的答案是强联通,那么任意一个点都能成为树根。dfs1找出所有的叶子即可。
正向用链式前向星,反向我用了动态数组,不必要。
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+5,M=1e5+5;
struct node{
int to,next;
}e[M];
int head[N],cnt;
void add(int x,int y){
e[cnt].to=y;
e[cnt].next=head[x];
head[x]=cnt++;
}
int leaf[N],vis[N];
int dfs1(int x){
int f=0;
for(int i=head[x];~i;i=e[i].next){
int ne=e[i].to;
if(vis[ne])continue;
vis[ne]=f=1;
dfs1(ne);
}
if(!f)leaf[x]=1;
}
vector<int>pre[N];
int dfs2(int x){
for(auto ne:pre[x]){
if(vis[ne])continue;
vis[ne]=1;
dfs2(ne);
}
}
int main()
{
int n,m,x,y;
while(~scanf("%d%d",&n,&m)){
if(n==0 && m==0)break;
for(int i=1;i<=n;i++)
head[i]=-1,vis[i]=leaf[i]=0,pre[i].clear();
cnt=0;
for(int i=1;i<=m;i++)
scanf("%d%d",&x,&y),add(x,y),pre[y].push_back(x);
int h=1;
vis[h]=1,dfs1(h);//找构造树,叶子节点
int f=1;
for(int i=1;i<=n;i++)//看是否连通
if(!vis[i])
f=0;
if(!f){
printf("No\n");
continue;
}
for(int i=1;i<=n;i++)vis[i]=0;
vis[h]=1;
dfs2(h);//反向搜是否根能到所有叶子
f=0;
for(int i=1;i<=n;i++)
if(leaf[i]&&!vis[i])
f=1;
if(f)printf("No\n");
else printf("Yes\n");
}
return 0;
}