判断单向连通图(拓扑排序+tarjan缩点)

题意: 给你一个有向图,如果对于图中的任意一对点u和v都有一条从u到v的路或从v到u的路,那么就输出’Yes’,否则输出’No’.

理解:当出现两个及以上入度为0的点(有一个就可能是别人到它,有两个的话那么那两个就互相到不了,因为他们入度都为0),就必定有不满足连通性的,但是如果只是单纯判断出度入度,会将重边误算,所以应该要用拓扑排序。拓扑排序之前,应该先进行缩点。

注意:多组数据,要记得清0!!

#include<bits/stdc++.h>
using namespace std;
const int N=1e3+5;
const int M=N*N;
int to[M],t[M],head[N],hea[N],nex[M],ne[M],tot,tott;
int cnt,T,stk[N],flag[N],low[N],vis[N],top,bel[N];
queue<int >q;
int in[N];
void add1(int a,int b)
{
    to[++tot]=b; nex[tot]=head[a]; head[a]=tot;
}
void add2(int a,int b)
{
    t[++tott]=b; ne[tott]=hea[a]; hea[a]=tott;
}
void init()
{
    top=0,tot=0,tott=0,cnt=0,T=0;
    memset(to,0,sizeof(to));memset(t,0,sizeof(t));
    memset(head,0,sizeof(head));memset(hea,0,sizeof(hea));
    memset(nex,0,sizeof(nex));memset(ne,0,sizeof(ne));
    memset(vis,0,sizeof(vis));memset(low,0,sizeof(low));
    memset(stk,0,sizeof(stk));memset(in,0,sizeof(in));
    memset(flag,0,sizeof(flag));
    while(!q.empty()) q.pop();
} 
void tarjan(int x)
{
    vis[x]=low[x]=++T;
    stk[++top]=x; flag[x]=1;
    for(int i=head[x];i;i=nex[i])
    {
        int v=to[i];
        if(!vis[v])
        { tarjan(v); low[x]=min(low[x],low[v]); }
        else if(flag[v])
        low[x]=min(low[x],vis[v]);
    }
    if(vis[x]==low[x])
    {
        cnt++;
        do{
            flag[stk[top]]=0; bel[stk[top]]=cnt;
        }while(stk[top--]!=x);
    }
}
void build(int u)
{
    for(int i=head[u];i;i=nex[i])
    if(bel[u]!=bel[to[i]])
    add2(bel[u],bel[to[i]]),in[bel[to[i]]]++;//bel[to[i]]!!
}
bool topo()
{
    //printf("%d\n",cnt);
    for(int i=1;i<=cnt;i++)
    if(!in[i]) q.push(i);
    while(!q.empty())
    {
        if(q.size()>1) return false;
        int u=q.front();
        q.pop();
        for(int i=hea[u];i;i=ne[i])
        {
            int v=t[i]; 
            in[v]--;
            if(!in[v]) q.push(v);
        }
    }
    return true;
}
int main()
{
    freopen("graph.in","r",stdin);
    freopen("graph.out","w",stdout);
    int n,m,Ti,a,b;
    scanf("%d",&Ti);
    while(Ti--)
    {
        init();
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
        scanf("%d%d",&a,&b),add1(a,b);
        for(int i=1;i<=n;i++)
        if(!vis[i])
        tarjan(i);
        for(int i=1;i<=n;i++)
        build(i);
        if(!topo()) printf("No\n");
        else printf("Yes\n");
    }
    
    
}
/*
2
3 2
1 3
2 3
3 2
1 2
2 3
*/

猜你喜欢

转载自www.cnblogs.com/mowanying/p/10650423.html