判断是不是一个环环间以点相连的有向图。
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;
}