前置知识:链式前向星
概念:存储带权值边的的数据结构,是邻接表的数组实现;head[i]表示以i为起点的最后一条边的编号,next表示相同起点的上一条边的编号。edge[j]即存储边的结构体数组数组,其中结构体含有终点,权值、以及next。
具体代码实现:
#include<bits/stdc++.h>//链式前向星
#define ll long long
#define maxn 20000
#define INF 0x3f3f3f3f
using namespace std;
struct Edge{
int to;
int val;
int next;
}edge[maxn];//next记录上一个有相同起点的边
int head[maxn];//head[i]记录起点为i的最后一条边
int tot=0,n,m;//tot是边的编号
void addedge(int u,int v,int w)
{
edge[++tot].to=v;
edge[tot].val=w;
edge[tot].next=head[u];//记录head[u]更新前最新的一条边
head[u]=tot;
}
int main()
{
for(int i=1;i<=n;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
addedge(u,v,w);
}
}
强连通分量:
-
概念:在一个有向图中,若该有向图中的任意两点可以互达,则该图强连通;强连通分量就是一个非强连通有向图中的最大强连通子图。
求强连通分量的方法:
kosaraju算法:
具体实现思路:
-
以任意一个顶点开始循环遍历每个顶点,每趟循环做一次dfs遍历走的到且未访问的点,每次dfs在回溯前将顶点压入栈内或给顶点一个靠前的编号。
反转图:即顶点不变,有向图的中的方向倒转后的图,如A->B变为B->A
-
第二次dfs,从点的栈顶开始遍历,其中从每个点做一次反转图的dfs遍历,每次dfs遍历能走到且未访问的点,每次dfs能访问到的点属于同一个强连通分量。
证明:
-
该算法能得到强连通分量:
已知第一次dfs,以一个点x为起点,在x之后被遍历到的点y也即栈中x底下的点,要么和x属于同一趟dfs,要么属于前几趟dfs,如果是在在后几趟dfs中,y一定在x的顶端,故栈中在x底下的点只可能是和x同一趟dfs或前几趟的点的点;同时还能意会到,前几趟dfs过的点要么是毫不相干的点,要么是保证一定有一条路线从x点能遍历到栈中在x底下的点。
第二次dfs从栈顶往栈底遍历,保证先遍历的点x在栈中底下的点y一定要么互不相干,要么已经能从x到y有路线。则dfs遍历的时候,一定只有可能遍历到从x到y有路线的点,若遍历到了,说明y->x也有路线,那么一定是属于同一个强连通分量内的
假设有两个点强连通,但未被计入同一个强连通分量中,可知第一次dfs,假设x先被遍历到,那么y一定在x的栈底,那么第二次dfs,y一定没有被遍历到,已知dfs会遍历所有在x栈底且能与x互达的的点,结论不成立,则一定能求出强连通分量。
代码实现:
#include<bits/stdc++.h>//scc——强连通分量
#define ll long long
#define maxn 20000
#define INF 0x3f3f3f3f
using namespace std;
struct Edge{
int to;
int val;
int next;
}edge1[maxn],edge2[maxn];//next记录上一个有相同起点的边
int head1[maxn],head2[maxn];//head[i]记录起点为i的最后一条边
int vis1[maxn]={0},vis2[maxn]={0};//方便dfs记录某个点是否已经遍历过
int trace[maxn];//按照搜索到的点先后顺序排列编号从1到n
int belong[maxn];//每个点属于哪个强连通分量编号从1到cnt2
int cnt1=0,cnt2=0,tot1=0,tot2=0,n,m;//tot是边的编号,cnt2保存强连通分量个数,cnt1保存边的个数
void addedge(int u,int v)
{
edge1[++tot1].to=v;
edge1[tot1].next=head1[u];//记录head[u]更新前最新的一条边
head1[u]=tot1;//记录正图
edge2[++tot2].to=u;
edge2[tot2].next=head2[v];
head2[v]=tot2;//记录反图
}
void dfs1(int u)
{
vis1[u]=1;
for(int i=head1[u];~i;i=edge1[i].next)
if(!vis1[edge1[i].to])
dfs1(edge1[i].to);
trace[++cnt1]=u;
}
void dfs2(int u)
{
vis2[u]=1;
belong[u]=cnt2;
for(int i=head2[u];~i;i=edge2[i].next)
if(!vis2[edge2[i].to])
dfs2(edge2[i].to);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=0;i<=n;i++)
head1[i]=head2[i]=-1;
for(int i=1;i<=m;i++)
{
int u,v,w;
scanf("%d%d",&u,&v);
addedge(u,v);
}
memset(vis1,false,sizeof(vis1));
memset(vis2,false,sizeof(vis2));
for(int i=1;i<=n;i++)
if(!vis1[i])
dfs1(i);
for(int i=cnt1;i>=1;i--)
if(!vis2[trace[i]])
{
cnt2++;
dfs2(trace[i]);
}
//输出
printf("%d\n",cnt2);
memset(vis1,0,sizeof(vis1));
for(int i=1;i<=n;i++)
if(!vis1[i])
{
for(int j=i;j<=n;j++)
if(belong[j]==belong[i])
{
printf("%d ",j);
vis1[j]=1;
}
puts("");
}
system("pause");
}