HDU 3594 Cactus 有向图判强连通及每条边是否仅属于一个环

判断是不是一个环环间以点相连的有向图。

Cactus的意思是仙人掌,不形象啊。

大概思路:

从起点0开始搜,搜到环就把环mark掉(环与搜索路径相连的点不mark,且从此点开始继续搜)。搜完后把所有搜到的点依次作为起点作如此搜索。如果搜到了之前mark的点(搜到起点不算)则说明有边存在于两个环。复杂度O(E)吧=。=

伪代码:

bool 函数(){

1、把起点0放入一个queue(stack也可以,无所谓,仅起储存作用,不过为了和后面的stack区分,就放入queue吧,实际上代码里也是stack:pstk);

2、当queue不为空,拿出front元素,记为start,从此点开始深搜,queue为空跳第7句;

3、搜索时要记录每个点当前搜索的边,下次从这点开始搜时就从这点的下一条边开始,那执行就可能会发现当前点已经没有边可搜了,那就看这个点是否是start点,不是的话返回0(说明不是强连通图,易证),是的话将其退栈,将其mark(事实上如果这点不是0的话,它必定已经被mark了),执行第2句;

4、把搜索的当前点放入stack,instk[u]=1,如果此点被mark了且不是start,返回0(说明有边存在于两个环,可证);  

5、如果搜到一个不在stack内的点,那就放入stack内,同时instk[u]=1,再同时把u放入队列,从这点开始搜,跳回第3句;

6、否则,这个点就是在stack内,把栈顶到此元素之间的所有点退栈(除了刚搜到的这个点),同时将其mark,从这点开始搜,继续从第3句开始执行;

7、扫描所有点,如有点没被mark,那就连弱连通也不是,返回0(代码中没加这句也过了,说明数据给出的都至少是弱连通的,懒得加了),否则返回1。

}

#include<iostream>
#include<queue>
using namespace std;
struct Edge{
    int u,v,next;
};
Edge edge[50000];
int N,head[20000],en;
void insert(int u,int v){
    edge[en].u=u;edge[en].v=v;edge[en].next=head[u];
    head[u]=en++;
}
void get_data(){
    memset(head,-1,sizeof(head));
    en=0;
    int a,b;
    scanf("%d",&N);
    while(scanf("%d%d",&a,&b),a||b){
        insert(a,b);
    }
}
int cur[20000],pstk[20000],psn,stk[20000],sn;
bool mark[20000],instk[20000];
bool run(){
    int i;
    for(i=0;i<N;i++)cur[i]=head[i];//初始化当前的边
    memset(mark,0,sizeof(mark));
    memset(instk,0,sizeof(instk));
    sn=psn=0;
    pstk[psn++]=0;
    int u,v,start;
    while(psn){
        start=u=pstk[--psn];//第2句
        if(cur[u]==-1)continue;
        while(true){
			//----第3句----
            if(cur[u]==-1){
                if(u==start){
                    sn--;
                    mark[u]=1;
                    break;
                }else return 0;
            }
			//--------------
            stk[sn++]=u;instk[u]=1;
            for(;cur[u]!=-1;){
                v=edge[cur[u]].v;cur[u]=edge[cur[u]].next;//把当前边设为下一条,为下次这一点的搜索作准备
                if(v!=start&&mark[v])return 0;//第4句
                if(!instk[v]){//第5句
                    pstk[psn++]=v;
                    u=v;break;
                }else{//第6句
                    while(stk[sn-1]!=v){
                        mark[stk[sn-1]]=1;
                        sn--;
                    }
                    u=v;
                }
            }
        }
    }
	//第7句 =。=
    return 1;
}
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        get_data();
        if(run())printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值