这几天填鸭赛让人很无语啊。。搞一些听都没听过是什么算法的题。。
所谓强连通,就是一个图中任意两点都是连通的,比如两个点a,b,既要有a到b的路径,又要有b到a的路径。Tarjan算法是用来算强连通分量的。
Tarjan算法有两个关键的数组,一个是DFN,记录结点的搜索时间编号,也就是第几个搜索到这个点的。还有一个是LOW,记录这个点能够搜索到的时间编号最小的栈中结点。每次搜索一个点时把这个点加到栈中。当一个点的LOW和DFN相等时,以这个点为根的子树属于一个强连通分量(因为如果不相等,两个点都在栈中,显然可以通过LOW的点可以搜索到DFN这个点,又可以从DFN这个点回溯到LOW的点,这个点必然不是根)。然后就把这个强连通分量都退栈。
Low(u)=Min
{ DFN(u), Low(v),(u,v)为树枝边,u为v的父节点 DFN(v),(u,v)为指向栈中节点的后向边(非横叉边) }
void Tarjan(int u) { int p,v; DFN[u]=LOW[u]=++time; instack[u]=true; stack[++top]=u; for(p=head[u]; p!=-1; p=e[p].next) { v=e[p].v; if(!DFN[v]) { Tarjan(v); if(LOW[v]<LOW[u]) LOW[u]=LOW[v]; } else if(instack[v]&&DFN[v]<LOW[u]) LOW[u]=DFN[v]; } if(DFN[u]==LOW[u]) { cnt++; do { v=stack[top--]; instack[v]=false; Belong[v]=cnt; } while(v!=u); } }Belong数组是记录当前结点属于哪一个强连通分量,到最后cnt就是强连通分量的个数。
填鸭赛是这么个题
Description
Input
Output
Sample Input
3 3 1 2 2 1 2 3
Sample Output
1
这道题还要缩点,把一个强连通分量缩成一个点,最后如果只有一个点没有出度,那么这个强连通分量包含元素的个数就是答案。判断一个点有没有出度,是看那个点里的元素连接的元素在不在一个Belong里,如果不在,就有出度。若只有一个点没有出度,记下这个点的Belong,再去找有多少个点的Belong等于它。
写这个的时候看网上有些用的动态分配地址的链表,有些用的什么vector。。不会=。= 不知道为什么一看到链表就觉得不会写,不过好在慢慢写一下还是写出了数组的链表。。
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<string> #include<algorithm> #include<map> #include<set> #define INF 0x3f3f3f3f #define MAXSTATE 2000007 using namespace std; struct edge { int v,next; } e[50010]; int cnt,top,time,head[10010],LOW[10010],DFN[10010],stack[10010],Belong[10010],de[10010]; bool instack[10010]; void Tarjan(int u) { int p,v; DFN[u]=LOW[u]=++time; instack[u]=true; stack[++top]=u; for(p=head[u]; p!=-1; p=e[p].next) { v=e[p].v; if(!DFN[v]) { Tarjan(v); if(LOW[v]<LOW[u]) LOW[u]=LOW[v]; } else if(instack[v]&&DFN[v]<LOW[u]) LOW[u]=DFN[v]; } if(DFN[u]==LOW[u]) { cnt++; do { v=stack[top--]; instack[v]=false; Belong[v]=cnt; } while(v!=u); } } int N,M; int main() { freopen("in.txt","r",stdin); while(scanf("%d%d",&N,&M)!=EOF) { int i,u,v,p,tot=0; memset(head,-1,sizeof(head)); memset(DFN,0,sizeof(DFN)); memset(de,0,sizeof(de)); memset(instack,false,sizeof(instack)); while(M--) { scanf("%d%d",&u,&v); e[tot].v=v; e[tot].next=head[u]; head[u]=tot++; } top=cnt=time=0; for(i=1; i<=N; i++) { if(!DFN[i]) Tarjan(i); } //下面是有出度的缩点都标记 for(i=1; i<=N; i++) for(p=head[i]; p!=-1; p=e[p].next) { if(Belong[i]!=Belong[e[p].v]) { de[Belong[i]]=1; break; } } int c=0,s,ans=0; for(i=1; i<=cnt; i++) if(!de[i]) { c++; s=i; //s是没有出度的缩点的编号 } if(c>1) printf("0\n"); //只有一个缩点没有出度的,答案就是这个点所包含的元素个数 else { for(i=1; i<=N; i++) if(Belong[i]==s) ans++; printf("%d\n",ans); } } return 0; }