20-2-26-tarjan算法-

(Lack a title here)

  • 有关tarjan算法过程的模拟戳我
    • 但是上面这篇博客并没有给出解释为什么这么做是可行的, 这个东西可以参照
    • <<算法竞赛入门经典训练指南>>中的解释Page321(第二版?)
    • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LjG9NX15-1582735515478)(在这里插入图片描述])
/*
 *Tarjan算法
 *复杂度O(N+M)
 */
const int MAXN=20010;//点数
const int MAXM=50010;//边数
struct Edge{
	int to,next;
}edge[MAXM];
int head[MAXN];
int tot;

int Low[MAXN];//每一个顶点在DFS搜索树中(所在强连通分量构成的子树)的根节点的时间戳
	//该节点的Low值和根节点的时间戳的值相同代表该节点和所属强连通分量的根节点
	//属于同一个强连通分量

int DFN[MAXN];//DFS第一次搜索到这个节点的时间戳
int Belong[MAXN];//Belong数组的值是1-scc
	//记录每一个顶点所属的强连通分量的序号
int scc;//记录强连通分量的个数

int Stack[MAXN];
int Index,top;

bool Instack[MAXN];
int num[MAXN];//各个强连通分量包含的点的个数, 数组的编号为[1,scc]
	//num不一定需要,根据实际情况

void addEdge(int u, int v){
	edge[tot].to=v;
	edge[tot].next=head[u];
	head[u]=tot++;
}

//从Tarjan算法从顶点u开始搜索
void Tarjan(int u){
	int v;
	Low[u]=DFN[u]=++Index;//搜索的第一个节点打上时间戳1
	Stack[top++]=u;//第一个节点入栈
	Instack[u]=true;

	//遍历栈顶顶点的所有临接的顶点DFS
	for(int i=head[u];i!=-1;i=edge[i].next){
		v=edge[i].to;
		if(!DFN[v]){//如果该节点第一次搜索到,从该节点开始搜索
			Tarjan(v);
			//根据子节点的时间戳更新该节点的时间戳,这里的时间戳指的是Low[]中的时间戳
				//若果下面的操作可以更新成功, 说明子节点找到了一条通往自己或者自己父亲
				//节点的路径
				//因为子节点tarjan过程中, 可能会找到子节点的祖先节点
					//从而更新其Low[]值, 故u需要根据子节点的更新, 调整
					//自己的Low的值
			Low[u]=min(Low[v],Low[u]);
		}
		//如果v节点在栈中, 说明这次搜索发现v是u的某个祖先节点
			//这样的话, 需要更新Low[u]=DFN[v];
		else if(Instack[v]&&Low[u]>DFN[v]){
			Low[u]=DFN[v];
		}
		//DFN[v]!=0 && !Instack[v]的不予处理
	}
	//相等说明该节点是该颗强连通分量子树的根
		//这时需要将该节点u的所有的子树和自己全部退栈.
	if(Low[u]==DFN[u]){
		scc++;
		do{
			v=Stack[--top];
			Instack[v]=fasle;
			Belong[v]=scc;
			num[scc]++;
		}while(v!=u);
	}
}
void solve(int N){
	memset(DFN,0,sizeof(DFN));
	memset(Instack,false,sizeof(Instack));
	memset(num,0,sizeof(num));
	Index=scc=top=0;
	for(int i=1;i<=N;i++){
		if(!DFN[i]){
			Tarjan(i);
		}
	}
}
void init(){
	tot=0;
	memset(head,-1,sizeof(head));
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值