poj 2762(强连通分量+拓扑排序)

题目链接:http://poj.org/problem?id=2762

题意:给出一个有向图,判断任意的两个顶点(u,v)能否从u到达v,或v到达u,即单连通,输出Yes或No.

分析:对于同一个强连通分量而言,所有的点都是互达的,如果该有向图只有一个强连通分量,则肯定是Yes了;

若有多个强连通分量呢?判断两个不同的强连通分量的点u和v是否单连通,缩点后,建新图,用拓扑排序判断,删除点的时候若发现有大于2个点的入度为0,则u和v必定不能连通。

AC代码:

  1 #include<cstdio>
  2 #include<cstring>
  3 const int N=1000+5;
  4 const int M=6000+5;
  5 struct EDGE{
  6     int v,next;
  7 }edge[M],edge2[M];
  8 int first[N],low[N],dfn[N],sta[M],belong[N],que[M],in[N],first2[N];
  9 bool instack[N],map[N][N];
 10 int cnt,g,scc,top,k;
 11 void AddEdge(int u,int v)
 12 {
 13     edge[g].v=v;
 14     edge[g].next=first[u];
 15     first[u]=g++;
 16 }
 17 void AddEdge2(int u,int v)
 18 {
 19     edge2[k].v=v;
 20     edge2[k].next=first2[u];
 21     first2[u]=k++;
 22 }
 23 int min(int a,int b)
 24 {
 25     return a<b?a:b;
 26 }
 27 void Tarjan(int u)    //求强连通分量
 28 {
 29     int i,v;
 30     low[u]=dfn[u]=++cnt;
 31     sta[++top]=u;
 32     instack[u]=true;
 33     for(i=first[u];i!=-1;i=edge[i].next)
 34     {
 35         v=edge[i].v;
 36         if(!dfn[v])
 37         {
 38             Tarjan(v);
 39             low[u]=min(low[u],low[v]);
 40         }
 41         else if(instack[v])
 42             low[u]=min(low[u],dfn[v]);
 43     }
 44     if(low[u]==dfn[u])
 45     {
 46         scc++;
 47         while(1)
 48         {
 49             v=sta[top--];
 50             instack[v]=false;
 51             belong[v]=scc;    //缩点
 52             if(v==u)
 53                 break;
 54         }
 55     }
 56 }
 57 void build(int n)    //建缩点后的新图
 58 {
 59     int u,i,v,a,b;
 60     memset(map,false,sizeof(map));
 61     memset(first2,-1,sizeof(first2));
 62     memset(in,0,sizeof(in));
 63     k=0;
 64     for(u=1;u<=n;u++)      //遍历每个顶点的出边
 65     {
 66         for(i=first[u];i!=-1;i=edge[i].next)
 67         {
 68             v=edge[i].v;
 69             a=belong[u];
 70             b=belong[v];
 71             if(a==b)     //若属于同一个强连通分量
 72                 continue;
 73             if(!map[a][b])
 74             {
 75                 AddEdge2(a,b);   //建新边
 76                 map[a][b]=true;
 77                 in[b]++;
 78             }
 79         }
 80     }
 81 }
 82 int topo()   //拓扑排序
 83 {
 84     int i,front,rear,top,v;
 85     front=rear=0;
 86     for(i=1;i<=scc;i++)
 87         if(in[i]==0)
 88         {
 89             que[rear++]=i;
 90         }
 91     if(rear-front>1)    //入度为0的顶点个数大于1,则无解
 92         return 0;
 93     while(front<rear)
 94     {
 95         top=que[front++];
 96         for(i=first2[top];i!=-1;i=edge2[i].next)
 97         {
 98             v=edge2[i].v;
 99             in[v]--;
100             if(in[v]==0)
101             {
102                 que[rear++]=v;
103             }
104             if(rear-front>1)
105                 return 0;
106         }
107     }
108     return 1;      //有解
109 }
110 int main()
111 {
112     int t,n,m,i,u,v;
113     scanf("%d",&t);
114     while(t--)
115     {
116         scanf("%d%d",&n,&m);
117         g=cnt=top=scc=0;
118         memset(first,-1,sizeof(first));
119         memset(dfn,0,sizeof(dfn));
120         memset(instack,false,sizeof(instack));
121         while(m--)
122         {
123             scanf("%d%d",&u,&v);
124             AddEdge(u,v);
125         }
126         for(i=1;i<=n;i++)    //求强连通分量
127             if(!dfn[i])
128                 Tarjan(i);
129         build(n);     //建缩点后的新图
130         if(topo())    //拓扑排序
131             printf("Yes\n");
132         else
133             printf("No\n");
134     }
135     return 0;
136 }
View Code

 

posted on 2013-10-22 19:46  jumpingfrog0  阅读(1061)  评论(0编辑  收藏  举报

导航