芝士:
有向图强连通分量在有向图G中,如果两个顶点vi,vj间(vi>vj)有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。有向图的极大强连通子图,称为强连通分量。
如图中1,2,3,4是一个强连通分量。
Kosaraju算法:
如果这是一个无向图,那么从一个节点出发,深搜得到的所有节点都是连通的。
但这是一个有向图,起始节点的不同会导致结果的不同,举个栗子,从5搜可以搜到6,但是从6搜不能搜到5。
这说明需要按照一个特定的顺序深搜。假设1,2,3,4是强连通分量a,5,6分别是强连通分量b,c。a可以搜到b,c,但b,c不能搜到a,由于我们不希望搜到不属于同一个强连通分量的点,所以会先搜b,c,再搜a。
那么这个顺序就是被指向的强连通分量要在指向的强连通分量之前被搜到,即被指向的强连通分量中的至少一个点在指向的强连通分量的任意一个点之前被搜到。
为了得到这个顺序,聪明的Kosaraju想到了一个方法:新建原图G的逆图GT(其定义为GT=(V,ET),ET={(u,v):(v,u)∈E}}),按照节点编号顺序在GT上深搜,每搜到一个节点,先把这个节点所能到达的所有未被访问过的节点加入栈中,再把自己加入栈中,然后按照从栈顶到栈底的顺序深搜,这样保证了在原图G中能到达我的点,都在我之后被搜到;
最后原图中强连通分量的个数就等于深搜的次数,每一次深搜到达的未被访问过的节点属于一个强连通分量(可以用一个数组记录一下);
模板:
/* 约翰的N (2 <= N <= 10,000)只奶牛非常兴奋,因为这是舞会之夜!她们穿上礼服和新鞋子,别 上鲜花,她们要表演圆舞. 只有奶牛才能表演这种圆舞.圆舞需要一些绳索和一个圆形的水池.奶牛们围在池边站好, 顺时针顺序由1到N编号.每只奶牛都面对水池,这样她就能看到其他的每一只奶牛. 为了跳这种圆舞,她们找了 M(2<M< 50000)条绳索.若干只奶牛的蹄上握着绳索的一端, 绳索沿顺时针方绕过水池,另一端则捆在另一些奶牛身上.这样,一些奶牛就可以牵引另一些奶 牛.有的奶牛可能握有很多绳索,也有的奶牛可能一条绳索都没有. 对于一只奶牛,比如说贝茜,她的圆舞跳得是否成功,可以这样检验:沿着她牵引的绳索, 找到她牵引的奶牛,再沿着这只奶牛牵引的绳索,又找到一只被牵引的奶牛,如此下去,若最终 能回到贝茜,则她的圆舞跳得成功,因为这一个环上的奶牛可以逆时针牵引而跳起旋转的圆舞. 如果这样的检验无法完成,那她的圆舞是不成功的. 如果两只成功跳圆舞的奶牛有绳索相连,那她们可以同属一个组合. 给出每一条绳索的描述,请找出,成功跳了圆舞的奶牛有多少个组合? 输入n,m,接下来m行 */ #include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<cmath> #define nn 10010 #define mm 100010 using namespace std; int e=0,l=0,ee=0,cir; int nx[mm],fi[nn],too[mm]; int fir[nn],nxt[mm],to[mm],li[nn]; bool vis[nn]; void add(int u,int v) { nxt[++e]=fir[u];fir[u]=e;to[e]=v; } void add2(int u,int v) { nx[++ee]=fi[u];fi[u]=e;too[e]=v; } void dfs(int s) { vis[s]=1; for(int i=fi[s];i;i=nx[i]) { if(!vis[too[i]]) dfs(too[i]); } li[++l]=s; } void dfs2(int s) { vis[s]=1;cir++; for(int i=fir[s];i;i=nxt[i]) { if(!vis[to[i]]) dfs2(to[i]); //写成了dfs } } int main() { int n,m,u,v,ma=-1,sum=0; scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { scanf("%d%d",&u,&v); add(u,v); add2(v,u); } for(int i=1;i<=n;i++) if(!vis[i]) dfs(i); fill(vis,vis+n+1,0); for(int i=l;i>=1;i--) if(!vis[li[i]]) //写成了vis[i] { cir=0; dfs2(li[i]); if(cir>=2) sum++; } printf("%d",sum); return 0; }
时间: 2024-10-09 08:52:09