【图论】Tarjan 缩点

【Tarjan】缩点

在一个点N数据极大的图中,直接SPFA或者记忆花搜索时间超限,那么我们可以利用Tarjan缩点来减少N。
举个例子;

如上图:3,6两点为该图中的强连通分量,我们可以将它们看做一个单元点。

怎么缩点呢

我们使用fa[]数组来存每个点所在的强连通分量中时间戳(DFN)最小的点,即将该点设为单元点。

怎么连边

如果两个点的fa[]不一样,且该两点间有一条有向边,那么我们就把他们的单元点(即fa[])连一条边

问题的求解

缩点后,重新建图,我们先找出图中入度为0的点,使用SPFA或拓扑排序来求解问题,与最短路问题相似

P3387 【模板】缩点

代码如下:

#include<bits/stdc++.h>
using namespace std;
struct edge{
	int nw,nxt,mark;
}pre[100010];
int n,m,idx,cnt;
int dfn[10010],low[10010];
int in[10010],v[10010],fa[10010];
int head[10010];
bool used[10010];
int stk[10010],p;
int ans=0;
void add (int x,int y,int cnt)
{
	pre[cnt].nw=x;
	pre[cnt].mark=head[x];
	pre[cnt].nxt=y;
	head[x]=cnt;
}
void tarjan (int u)
{
	dfn[u]=low[u]=++idx;
	stk[++p]=u;
	used[u]=1;
	for (int i=head[u];i!=0;i=pre[i].mark)
	{
		int nx=pre[i].nxt;
		if (!dfn[nx])
		{
			tarjan (nx);
			low[u]=min (low[u],low[nx]);
		}
		else if (used[nx])
			low[u]=min (low[u],dfn[nx]);
	}
	if (low[u]==dfn[u])
	{
		do{
			v[u]+=v[stk[p]];
			fa[stk[p]]=u;
			used[stk[p]]=0;
			p--;
		}while (stk[p+1]!=u);
		v[u]>>=1;
	}
}
int topo ()
{
	int dis[10010];
	queue<int>q;
	for (int i=1;i<=n;i++)
		if (fa[i]==i)
		{
			dis[i]=v[i];
			if (!in[i])
				q.push(i);
		}
	while (!q.empty())
	{
		int Now=q.front();
		for (int i=head[Now];i!=0;i=pre[i].mark)
		{
			int Nxt=pre[i].nxt;
			dis[Nxt]=max (dis[Nxt],dis[Now]+v[Nxt]);
			in[Nxt]--;
			if (!in[Nxt])
				q.push (Nxt);
		}
		q.pop();
	}
	int maxx=0;
	for (int i=1;i<=n;i++)
		if (fa[i]==i)
			maxx=max (maxx,dis[i]);
	return maxx;
}
int main()
{
	memset (in,0,sizeof (in));
	memset (used,0,sizeof (used));
	memset (dfn,0,sizeof(dfn));
	memset (head,0,sizeof (head));
	scanf ("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
		scanf ("%d",&v[i]);
	for (int i=1;i<=m;i++)
	{
		int a,b;
		scanf ("%d%d",&a,&b);
		add (a,b,i);
	}
	for (int i=1;i<=n;i++)
		if (!dfn[i])
			tarjan (i);
	memset (head,0,sizeof (head));
	for (int i=1;i<=m;i++)
	{
		int Now=fa[pre[i].nw];
		int Nxt=fa[pre[i].nxt];
		if (Now!=Nxt)
		{
			add (Now,Nxt,++cnt);
			in[Nxt]++;
		}
	}
	printf ("%d",topo ());
	return 0;
}
原文地址:https://www.cnblogs.com/PaulShi/p/10056836.html