tarjan复习

@

Tarjan 求 LCA

做法

总体思想:遍历每一个结点并使用并查集记录父子关系。

Tarjan 是一种 DFS 的思想。我们需要从根结点去遍历这棵树。

当遍历到某一个结点(称之为 \(x\)) 时,你有以下几点需要做的。

  1. 将当前结点标记为已经访问。

  2. 递归遍历所有它的子节点(称之为 \(y\)),并在递归执行完后用并查集合并 \(x\)\(y\)

  3. 遍历与当前节点有查询关系的结点(称之为 z)(即是需要查询 LCA 的另一些结点),如果 \(z\) 已经访问,那么 \(x\)\(z\)\(LCA\) 就是 \(getfa(z)\)(这个是并查集中的查找函数),输出或者记录下来就可以了。

这是伪代码

void tarjan(int x){
    //在本代码段中,s[i]为第i个子节点 , t[i]为第i个和当前节点有查询关系的结点。
    vis[x]=1;//标记已经访问,vis是记录是否已访问的数组
    for (i=1;i<=子节点数;i++){//枚举子节点 (递归并合并)
        tarjan(s[i]);
        marge(x,s[i]);//并查集合并
    }
    for (i=1;i<=有查询关系的结点数;i++){
        if (vis[t[i]]){
            cout<<x<<"和"<<t[i]<<"的LCA是"<<getfa(t[i])<<endl;//如果t[i]已经访问了输出(getfa是并查集查找函数)
        }
    }
}

tarjan求强连通分量

点我(())进入

先最初调用

1、init()

2、把图用add 存下来,注意图点标为1-n,若是[0,n-1]则给所有点++;

3、调用tarjan_init(n) 再调用suodian()

4、新图就是vectorG[]; 新图点标从1-tar

5、对于原图中的每个点u,都属于新图中的一个新点Belong[u]

新图一定是森林。

6、新图中的点u 所表示的环对应原图中的vector bcc[u]

7、旧图中u在新图中所属的点是Belong[u]

作者:九野的博客
来源:CSDN
原文:https://blog.csdn.net/acmmmm/article/details/9963693

提供作者本人的模板:

#include<iostream>
#include<cstdio>
#include<queue>
#include<algorithm>
#include<cmath>
using namespace std;
int n,m,w[100001],visit[100001],head[100001],dfn[100001],low[100001],co[100001],si[100001],st[100001],col,num,dis[100001],vis[100001],w1[100001],re[100001],v[100001],ru[100001],dai[100001],vis2[100001];
int top=0;
int e[100001],h[500001];
struct node{
    int from,next,to;	
}edge[500001],ed[500001];
void add(int from,int to){
    edge[++top].from=from;
    edge[top].to=to;
    edge[top].next=head[from];
    head[from]=top;
}
void tarjan(int now){
    dfn[now]=++num;
    low[now]=num;
    st[++top]=now;
    for(int i=head[now];i;i=edge[i].next){
        int to=edge[i].to;
        if(!dfn[to]){
            tarjan(to);
            low[now]=min(low[now],low[to]); 
        }
        else if(!co[to]){
            low[now]=min(low[now],dfn[to]);
        }
    }
    if(low[now]==dfn[now]){
        co[now]=++col;
        dai[col]=now;
        w1[col]+=w[now];
        ++si[col];
        while(st[top]!=now){
            co[st[top]]=col;
            ++si[col];
            w1[col]+=w[st[top]];
            --top;
        }
        --top;
    }
}
int topo(){
    queue<int>q;
    for(int i=1;i<=col;i++){
        if(ru[i]==0) q.push(i);
        e[i]=w1[i];
    }
    while(!q.empty()){
        int now=q.front();
        q.pop();
        for(int i=h[now];i;i=ed[i].next){
            int to=ed[i].to;
      	    e[to]=max(e[to],e[now]+w1[to]);
      	    ru[to]--;
      	    if(ru[to]==0) q.push(to);
        }
    }
    int maxn=0;
    for(int i=1;i<=col;i++){
    	maxn=max(maxn,e[i]);
    }
    return maxn;
}
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>w[i];
    }
    for(int i=1;i<=m;i++){
        int u,v;
        cin>>u>>v;
        add(u,v);
        re[v]++;
    }
    top=0;
    for(int i=1;i<=n;i++){
    	if(!dfn[i]) tarjan(i);
    }
    for(int i=1;i<=m;i++){
    	int from=co[edge[i].from],to=co[edge[i].to];
    	if(from!=to){
    		ed[++top].from=from;
    		ed[top].to=to;
    		ed[top].next=h[from];
    		h[from]=top;
    		ru[to]++;
        }
    }
    cout<<topo();
    return 0;
}

割点

可以使用Tarjan算法求割点(注意,还有一个求连通分量的算法也叫Tarjan算法,与此算法类似)。(Tarjan,全名Robert Tarjan,美国计算机科学家。)

首先选定一个根节点,从该根节点开始遍历整个图(使用DFS)。

对于根节点,判断是不是割点很简单——计算其子树数量,如果有2棵即以上的子树,就是割点。因为如果去掉这个点,这两棵子树就不能互相到达。

对于非根节点,判断是不是割点就有些麻烦了。我们维护两个数组dfn[]和low[],dfn[u]表示顶点u第几个被(首次)访问,low[u]表示顶点u及其子树中的点,通过非父子边(回边),能够回溯到的最早的点(dfn最小)的dfn值(但不能通过连接u与其父节点的边)。对于边(u, v),如果low[v]>=dfn[u],此时u就是割点。

但这里也出现一个问题:怎么计算low[u]。

假设当前顶点为u,则默认low[u]=dfn[u],即最早只能回溯到自身。

有一条边(u, v),如果v未访问过,继续DFS,DFS完之后,low[u]=min(low[u], low[v]);

如果v访问过(且u不是v的父亲),就不需要继续DFS了,一定有dfn[v]<dfn[u],low[u]=min(low[u], dfn[v])。

#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
int read(){
	int x=0,pos=1;char ch=getchar();
	for(;!isdigit(ch);ch=getchar()) if(ch=='-') pos=0;
	for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
	return pos?x:-x;
}
const int N=20005,M=100005;
int head[N],top=0;
struct node{
	int v,nex;
}edge[M<<1];
void add(int u,int v){
	edge[++top].v=v;
	edge[top].nex=head[u];
	head[u]=top;
}
int n,m,cnt,dfn[N],low[N],vis[N],tot=0,ans[N],num;
void dfs(int u,int rt){
	int pos=0;
	dfn[u]=++cnt;
	low[u]=cnt;
	for(int i=head[u];i;i=edge[i].nex){
		int v=edge[i].v;
		if(!dfn[v]){
			if(u==rt) tot++;
			dfs(v,rt);
			low[u]=min(low[u],low[v]);
			if(low[v]>=dfn[u]&&u!=rt&&!pos){
				pos=1;
				ans[++num]=u;
			}
		}
		else low[u]=min(low[u],dfn[v]);
	}
}
int cmp(int aa,int bb){
	return aa<bb;
}
int main(){
	n=read(),m=read();
	for(int i=1;i<=m;i++){
		int x=read(),y=read();
		add(x,y);
		add(y,x);	
	}
	for(int i=1;i<=n;i++){
		if(!dfn[i]){
			tot=0;
			dfs(i,i);
			if(tot>=2){
				ans[++num]=i;
			}
		}
	}
	sort(ans+1,ans+num+1,cmp);
	printf("%d\n",num);
	for(int i=1;i<=num;i++){
		printf("%d ",ans[i]);
	}
	return 0;
}
posted @ 2019-07-27 22:56  lcyfrog  阅读(88)  评论(0编辑  收藏  举报