有向图的强连通分量

//HDU 1269
//因为HDU挂了所以下面求有向图的强连通分量的代码还没有提交,等好了再测试吧

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
using namespace std;

#define MAXN 10010
#define MAXM 100010

//求图上的强连通分量 Tarjan算法
//思想 : 对每个点深搜一遍求改点能否组成一个强连通分量
//复杂度 O(E + V )  
/*
强连通:两个点从一个点总可以到达另外一个点就代表两个点强连通,相当于两个点在有向图里面成环
强连通图:一个有向图中,任意两个点都强连通,则这个图是强连通图
强连通分量:非强连通图中一个极大强连通图就是这个图的强连通分量,一个非强连通图可能有多个强连通分量,一个点也可以是一个强连通分量
*/ 


struct Edge{
    int v, next;  
}edge[MAXM];    //边结点数组 

// head[]头结点数组
int head[MAXN], tot ;

//stack[]为栈,DFN[]为深搜次序数组并且可以判定点是否被访问过,Belong[]为每个结点所对应的强连通分量标号数组 
// Low[u]为u结点或者u的子树结点所能追溯到的最早栈中结点的次序号,也就是u节点所能追溯到的最远的强连通分量的根节点 
int stack[MAXN], DFN[MAXN], Low[MAXN], Belong[MAXM];
//instack[]为是否在栈中的标记数组 
int instack[10010];
int n, m, index, scc, top ;

void init(){
    index = 0;
    //初始化连通分量标号,次序计数器,栈顶指针为0 
    scc = top = tot = 0; 
    memset(head, -1, sizeof(head));
    //结点搜索的次序编号数组为0,同时可以当是否访问的数组使用 
    memset(DFN, 0, sizeof(DFN)); 
}

//链式前向星 
void addedge(int u, int v){
    edge[tot].v = v;
    edge[tot].next = head[u];
    head[u] = tot++;
}


//Tarjan算法求有向图的强连通分量,求每个图的强联通分量的个数和每个强连通分量的节点 
void Tarjan(int u){
    //index为时间戳
    DFN[u] = Low[u] = ++index;
    //标记在栈中 
    instack[u] = 1;
    //入栈
    stack[top++] = u; 
    //枚举u的每一条边 
    for(int e = head[u]; e != -1; e = edge[e].next){
        //u所邻接的边 
        int v = edge[e].v;
        //未被访问
        //树枝边
        if(!DFN[v]){
            //继续向下找
            Tarjan(v); 
            // 更新结点u所能到达的最小次数层 
            //更新强连通分量的根节点 
            //进行这一步之前一定有一条回向边将v节点的最早节点更新了
            if(Low[u] > Low[v]) Low[u] = Low[v];
        }
        //如果v结点在栈内,扩展v节点所能追溯得到强连通分量的根节点 
        //回向边,回向边(u,v)的编号小的节点v的编号一定比回向边另外一个节点u能最早追溯的节点小
        else if(instack[v] && DFN[v] < Low[u]) 
            Low[u] = DFN[v];
    }
    //如果节点u是强连通分量的根
    if(DFN[u] == Low[u]){
        //连通分量标号加1 
        scc++; 
        int t ;
        do{
            //退栈 
            t = stack[--top];
            //标记不在栈中 
            instack[t] = 0;
            //出栈结点t属于index标号的强连通分量 
            Belong[t] = scc;
        }
        //直到将u从栈中退出
        while(t != u); 
    }
}

void solve(){
    //枚举每个结点,搜索连通分量
    for(int i = 1; i <= n; i++)
        //未被访问 
        if(!DFN[i])  
            //则找i结点的连通分量
            Tarjan(i); 
}

int main(){
    while(scanf("%d%d",&n,&m) && (n || m)){
        init();
        while(m--){
            int u, v;
            scanf("%d%d", &u, &v);
            addedge(u, v);
        }
        //求强连通分量 
        solve(); 
        //只有一个强连通分量,说明此图各个结点都可达
        if(scc == 1) printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std ;

const int maxn = 1e4 + 10 ;
const int maxm = 1e5 + 10 ;

/*
有向图的强连通分量: kosaraju 双向dfs
复杂度 : O( E + V ) 
*/ 


//edge1原图,edge2原图的逆图 
struct Edge{
    int to , next ;
} edge1[maxm] , edge2[maxm];

int head1[maxn ] , head2[maxn] ;
bool mark1[maxn] , mark2[maxn] ;
int tot1 , tot2 ;
int cnt1 , cnt2 ;

//对原图dfs的后分配编号 
int st[maxn] ;
//belong求每个点属于哪个强连通分量 
int belong[maxn] ;
//某个强连通分量的点的个数 
int num ;
//某个强连通分量的点的个数 
int setnum[maxn] ;

void addedge( int u , int v ){
    edge1[tot1].to = v ; edge1[tot1].next = head1[u] ; head1[u] = tot1 ++ ;
    edge2[tot2].to = u ; edge2[tot2].next = head2[v] ; head2[v] = tot2 ++ ;
}

//对原图dfs,第一次深搜将所有节点划分到不同的有向子树 
void dfs1(int u ){
    mark1[u] = true ;
    for(int i = head1[u] ; i != -1 ; i= edge1[i].next )
        if( !mark1[edge1[i].to ])
            dfs1( edge1[i].to ) ;
    st[cnt1 ++ ] = u ;
} 

//第二次深搜对之前建立的有向子树再次建立有向子树,那么同处于两次有向子树的所有节点构成一个强连通分量
void dfs2( int u ){
    mark2[u] = true ;
    //num代表该强连通分量的节点个数 
    num ++ ;
    //cnt2代表该节点所处的强连通分量的编号 
    belong [u ] = cnt2 ;
    for(int i = head2[u] ; i != -1 ; i = edge2[i].next )
        if( !mark2[edge2[i].to])
            dfs2( edge2[i].to ) ;
} 

void solve( int n ){
    memset( mark1 , false , sizeof( mark1 )) ;
    memset( mark2 , false , sizeof( mark2 )) ;
    cnt1 = cnt2 = 0 ;
    for(int i = 1 ; i <= n ;i++)
        if( !mark1[i] )
            dfs1(i) ;
    for(int i = cnt1-1 ; i>= 0 ; i--)
        if( !mark2[st[i]]){
            // st[i] 划分一个连通分量 
            num = 0 ;
            dfs2( st[i]) ;
            setnum[cnt2 ++] = num ; 
        } 
}

int n, m ;

void ini() {
    memset( head1 , -1 , sizeof( head1 )) ;
    memset( head2 , -1 , sizeof( head2 )) ;
}
int main(){
    while(scanf("%d%d",&n,&m) && (n || m)){
        ini() ;
        while(m--){
            int u, v;
            scanf("%d%d", &u, &v);
            addedge(u, v);
        }
        //求强连通分量 
        solve( n ); 
        //只有一个强连通分量,说明此图各个结点都可达
        if(cnt2 == 1) printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值