Tarjan缩点

从上周五开始学缩点,今天终于把代码敲过了,再次做一个总结(有好多问题需要总结)。
概念:
缩点:在一张有向图中,将图中的强连通分量缩成一个点。
连通分量:一个点集,是的点集中的点两两之间可以互相抵达。
强连通分量:这个点集中无法再加入一个新的点。
思路:
1.从一个未搜过的点开始,将这个点标记为正在搜的点,并用另一数组记录这个点为第几个被搜到的点(dfn),把这个点放入栈中。
2.搜这个点能到达的所有的点,并用一个数组记录它能到的所有的点中dfn最小的一个(low)(能到的所有的点包括未搜的和正在搜的)。
3.在搜完后判断如果这个点的dfn和low相等说明从栈顶到这个元素属于同一个强连通分量,并把它们从这个栈中删掉,将这些点标记为搜完的点。
我在学缩点的期间遇到的一些问题:
1.

从点1开始,先查2,再查3,再查4,又查到1,返回到1,接着查5,返回来dfn[1]是等于low[1]的,所以这是一个强连通分量,但是点1,2,3,4是一个强连通分量,不包括5。
解释:判断点1前就已经判断了点5,而且已经将点5自己判断为一个强连通分量,出栈了,所以记录时会将点1,2,3,4记录为一个强连通分量,点5记录为一个强连通分量。
2.

我原来的时候一直在一个点搜完后将它标记为搜完,而不是在它出栈时标记,就一直错。
解释:根据上面这个图,点1,2,3,4显然是一个强连通分量,但是如果在一个点搜完后就立刻将它标记为搜完,点4就不会搜点3了,这样就会将点4自己标记为一个强连通分量所以要在它出栈时再将它标记为搜完。
下面是代码:

点击查看代码
#include<iostream>
using namespace std;
int n,m;
int a[10010];
int head[10010],tot;
int shead[10010],stot;
struct node{
	int to;
	int nxt;
}edge[100010],sedge[100010];
void build(int u,int v){
	edge[++tot].to=v;
	edge[tot].nxt=head[u];
	head[u]=tot;
}
void addedge(int u,int v){
	sedge[++stot].to=v;
	sedge[stot].nxt=shead[u];
	shead[u]=stot;
}
int dfn[10010],low[10010],cnt;
int zhen[100010],top;
int dian[10010],shu;
int dui[10010];
int ru[10010];
int vis[10010];
void dfs(int u){
	low[u]=dfn[u]=++cnt;
	zhen[++top]=u;
	vis[u]=1;
	for(int i=head[u];i;i=edge[i].nxt){
		int v=edge[i].to;
		if(dfn[v]==0){
			dfs(v);
			low[u]=min(low[v],low[u]);
		}
		else if(vis[v]==1)low[u]=min(low[u],low[v]);
	}
	if(low[u]==dfn[u]){
		shu++;
		while(zhen[top]!=u){
			vis[zhen[top]]=0;
			dian[shu]+=a[zhen[top]];
			dui[zhen[top]]=shu;
			top--;
		}
		top--;
		dui[u]=shu;
		dian[shu]+=a[u];
		vis[u]=0;
	}
}
int maxn;
int f[10010];
void tuopu(int u){
	for(int i=shead[u];i;i=sedge[i].nxt){
		int v=sedge[i].to;
		if(f[u]+dian[v]>f[v]){
			f[v]=f[u]+dian[v];
			maxn=max(maxn,f[v]);
			tuopu(v);
		}
	}
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	for(int i=1;i<=m;i++){
		int u,v;
		cin>>u>>v;
		build(u,v);
	}
	for(int i=1;i<=n;i++){
		if(dfn[i]==0){
			dfs(i);
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=head[i];j;j=edge[j].nxt){
			int v=edge[j].to;
			if(dui[v]!=dui[i]){
				addedge(dui[i],dui[v]);
				ru[dui[v]]++;
			}
		}
	}
	for(int i=1;i<=shu;i++){
		if(ru[i]==0){
			f[i]=dian[i];
			maxn=max(f[i],maxn);
			tuopu(i);
		}
	}
	cout<<maxn;
	return 0;
}
题目可以看洛谷P3387。
posted @ 2022-03-28 11:42  zzzzzz2  阅读(29)  评论(0编辑  收藏  举报