Tarjan算法可用于求一个图(有向或者无向)的联通分量,完成“缩点”的操作。
板子:
int dfn[N],low[N],tmp;//时间戳、可经过的最小时间戳
int stk[N],top;
bool in_stk[N];
int id[N],scc_cnt;
void tarjan(int u){
dfn[u]=low[u]=++tmp;
stk[++top]=u,in_stk[u]=true;
for(int i=head[u];i;i=ne[i]){
int j=to[i];
if(!dfn[j]){
tarjan(j);
low[u]=min(low[u],low[j]);
}
else if(in_stk[j]) low[u]=min(low[u],dfn[j]);
}
if(low[u]==dfn[u]){
int y;
++scc_cnt;
do{
y=stk[top--];//出栈的点都是当前这个联通分量里的点
in_stk[y]=false;
id[y]=scc_cnt;
}while(y!=u);
}
}
但是对于一个不连通的图,则需要对每一个还未打上时间戳的点进行tarjan递归操作。
重要结论:缩点后得到的强连通分量的倒序就是缩点后得到的DAG的拓扑序
例题1:AcWing 367.学校网络
看到这个题目的第一问就很容易想到去缩点,把该图变成一个DAG,然后求出起点个数即可。第二问有一些难想,但只要我们想到:把起点和终点全部首尾相连就是一种可行的方案,并且这种可行的方案也满足最优性,因为只要有一个起点或者终点没有连起来,就一定不是合法的方案,所以最终答案是起点个数与终点个数的最大值。代码如下:
#include<iostream>
using namespace std;
const int N=105,M=1e4+5;
int head[N],to[M],ne[M],idx;
void add(int x,int y){
ne[++idx]=head[x];
to[idx]=y;
head[x]=idx;
}
int dfn[N