POJ 2553 强连通分量 Tarjan

很悲剧的读错题了。本来以为和Popular Cow一样,对于所有的点 都被可达这样的点为sink点。实际上的意思是对于所有的w点到v点有路径且v到w也有路径这样的点才叫sink点,所以其在w->v有边而v->w没有边,这样的点不是sink点。当且仅当上述两个条件都满足时,才为sink点。

对于这题的分析:

首先进行判断就是e(a->b),e(b->c)=>e(a->c)也就是边存在传递关系,这样可达关系是可以传递的。当然可以通过bellman进行可达性的传递。但是显然实现效率十分低。于是乎,我们可以将图缩为很多的强连通分量,通过强连通分量之间的关系来代表其中每个点之间的关系,这样就不用麻烦的去传递边的关系了,可以把每个点的信息都交给自己的缩点。若S1->S2,也就是说S1中的所有点到S2都存在边,而S2->S1是没有边的(不然S1,S2就属于一个强连通了),显然S1中的所有点都不符合sink的条件。这题就转化为了求没有出度的强连通分量中的所有点,按大小输出就行了。

这题要注意的关键是,不用真的去缩点,而是用一个P数组将所有的点所处的强连通分量的标号储存就行了。通过每个点每条边的关系,如果该点有出边,而且出边是使该强连通连接到其他的强连通的,这样才算是该强连通有出度,将该强连通标记,再遍历点,输出相应的没有出度的强连通中的点就好了。

//#include<iostream>
#include<stdio.h>
#include<stack>
#define MAXN 5005
using namespace std;

struct Node
{
       int v;
       Node *next;
}Edge[MAXN*MAXN],*ptr[MAXN];

int V,E;
int DFN[MAXN],LOW[MAXN],SCC[MAXN],P[MAXN];
bool visited[MAXN],inS[MAXN];
int SCCNum;
stack<int>myStack;
int min( int a,int b ){ return a<b?a:b; }

void addEdge( int u,int v,int num )
{
     Node *p=&Edge[num];
     p->v=v;
     p->next=ptr[u];
     ptr[u]=p;
}

int cnt;
void Tarjan(int pre)
{
     LOW[pre]=DFN[pre]=++cnt;
     Node *p=ptr[pre];
     myStack.push(pre);
     visited[pre]=true;
     inS[pre]=true;
     int u=pre;
     
     while( p )
     {
            if( !visited[p->v] )
            {
                Tarjan(p->v);
                LOW[u]=min( LOW[u],LOW[p->v] );
            }
            else if( inS[p->v] )
                 LOW[u]=min( LOW[u],DFN[p->v] );
            p=p->next;
     }
     if( DFN[u]==LOW[u] )
     {
         int v;
         SCCNum++;
         do {
             v=myStack.top();
             myStack.pop();
			 inS[v]=false;
             SCC[SCCNum]++;
             P[v]=SCCNum;           
         }while( u!=v );
     }
}
bool FINISH;
bool DFS( int pre )
{
     visited[pre]=true;
     int sum=0,i;
     for( i=1;i<=SCCNum;i++ )
          sum+=visited[i];
     if( sum==SCCNum )
         return FINISH=true;
     Node *p=ptr[pre];
     while( p )
     {
            if( !visited[p->v] )
                DFS(p->v);
            if( FINISH )
                return true;
            p=p->next;
     }
     return false;
}

int main()
{
    while( scanf( "%d",&V )!=EOF )
    {
           if( !V )
               break ;
           scanf( "%d",&E );
           int i,j;
           int u,v;
           for( i=0;i<=V;i++ )
                ptr[i]=NULL;
           cnt=0;SCCNum=0;FINISH=false;
           while( !myStack.empty() ) myStack.pop();
           
           for( i=1;i<=E;i++ )
           {
                scanf( "%d %d",&u,&v );
                addEdge( u,v,i );
           }
           
           memset( DFN,0,sizeof(DFN) );
           memset( LOW,0,sizeof(LOW) );
           memset( visited,0,sizeof(visited) );
           memset( inS,0,sizeof(inS) );
           
           for( i=1;i<=V;i++ )
                if( DFN[i]==0 ) 
					Tarjan(i);
                
           bool outD[MAXN];
           memset( outD,0,sizeof(outD) );
           for( int i=1;i<=V;i++ )
           {
                Node *p=ptr[i];
                while( p )
                {
                       if( P[i]!=P[p->v] )
                           outD[ P[i] ]=true;
                       p=p->next;
                }
           }
           for( i=1;i<=V;i++ )
           {
                if( outD[P[i]]==false )
                {
                    printf( "%d",i );break;
                }
           }
           for( i++;i<=V;i++ )
           {
                if( outD[P[i]]==false )
                printf( " %d",i );
           }
           printf( "\n" );
    }
    return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值