tarjan:可以把图分为若干个强连通分量(每个分量里的任意点都可以相互到达即为强连通分量,一个点也可以算作强连通分量)
核心
void tarjan(int x)
{
dfn[x] = low[x] = ++tot;
s.push(x),vis[x] = 1;
for (int i = 0;i < g[x].size();i++){
int v = g[x][i];
if(!dfn[v])
{
tarjan(v);
low[x] = min(low[x],low[v]);
}
else if(vis[v]){
low[x] = min(low[x],dfn[v]);
}
}
if(dfn[x] == low[x]){
cnt++;
while (1){
int now = s.top();
s.pop();
vis[now] = 0;//标记是否在栈中
num[cnt]++;//num计算每个强连通分量的顶点个数
id[now] = cnt;//id存放该顶点所在的强连通分量
if(now==x) break;
}
}
}
通过tarjan求出入度
for (int i = 1;i <= n;i++){
for (int j = 0;j < g[i].size();j++){
int u = id[i],v = id[g[i][j]];//u,v代表i,j顶点所在的强连通分量
if(u!=v){
out[u]++;
in[v]++;
}
}
}
求割边
void dfs(int cur,int father)//当前顶点的编号和父顶点的编号
{
int i;
dfn[cur]=low[cur]=++inde;
for(i=head[cur]; i!=-1; i=edge[i].next)
{
int to=edge[i].to;
if(dfn[to]==0)//如果顶点to没有被访问过,此时to为cue的儿子
{
dfs(to,cur);//继续dfs
//更新cur能访问的最早顶点的时间戳
low[cur]=min(low[cur],low[to]);
//满足low[to]>dfn[cur]
if(low[to]>dfn[cur])
ans++;
}
else if(to!=father)//如果to被访问过,则to为cur的祖先,要更新节点
{
low[cur]=min(low[cur],dfn[to]);
}
}
return;
}
求割点
void dfs(int cur,int father)//当前顶点的编号和父顶点的编号
{
int child = 0,i;//child用来记录在生成树中当前顶点cur的儿子个数
dfn[cur]=low[cur]=++inde;
for(i=head[cur]; i!=-1; i=edge[i].next)
{
int to=edge[i].to;
if(dfn[to]==0)//如果顶点to没有被访问过,此时to为cue的儿子
{
child++;
dfs(to,cur);//继续dfs
//更新cur能访问的最早顶点的时间戳
low[cur]=min(low[cur],low[to]);
//当前节点不是根节点并且满足low[to]>=dfn[cur],则当前顶点为割点
if(cur!=root && low[to]>=dfn[cur])
flag[cur]=1;
//当前节点为根节点,需要有两个儿子才是割点,当有两个以上儿子时,这句话之前肯定会被执行
if(cur==root && child==2)
flag[cur]=1;
}
else if(to!=father)//如果to被访问过,则to为cur的祖先,要更新节点
{
low[cur]=min(low[cur],dfn[to]);
}
}
return;
}