1. 程式人生 > 實用技巧 >tarjan演算法與拓撲排序

tarjan演算法與拓撲排序

演算法介紹

tarjan

tarjan演算法要求使有向圖。
Tarjan就是一個輔助作用,把有環圖縮為無環圖,也就是將強聯通分量縮成一個點。
幾個陣列 dfn時間戳,low仍在棧中的最小時間戳,dag縮點後的陣列,ins是否在棧中。

void tarjan(int x)
{

	dfn[x]=low[x]=++cnt;
	ins[x]=1;
	st.push(x);
	for(int i=0;i<g[x].size();i++)
	{
		int q=g[x][i];
		if(dfn[q]==0)
		{
			tarjan(q);
			low[x]=min(low[x],low[q]);		
		}
		else if(ins[q]==1)
		{
			low[x]=min(low[x],dfn[q]);
		}
	}
	if(dfn[x]==low[x])
	{
		numb++;
		int q;
		do
		{
			q=st.top();
			st.pop();
			ins[q]=0;
			dag[q]=numb;
			num[numb]++;
			p[numb]+=a[q];
		}
		while(q!=x);
	}

}

拓撲排序

對一個有向無環圖(Directed Acyclic Graph簡稱DAG)G進行拓撲排序,是將G中所有頂點排成一個線性序列,使得圖中任意一對頂點u和v,若邊<u,v>∈E(G),則u線上性序列中出現在v之前。通常,這樣的線性序列稱為滿足拓撲次序(Topological Order)的序列,簡稱拓撲序列。簡單的說,由某個集合上的一個偏序得到該集合上的一個全序,這個操作稱之為拓撲排序。
拓撲排序步驟
統計入度,入度為0入棧,遞推並更新入度,入度為0入棧,棧空結束

queue<int>q;
	int tot=0;
	for(int i=1;i<=numb;i++)
	{
		if(!ind[i])
		{
			q.push(i);
			dist[i]=p[i];
		}
	}
	while(!q.empty())
	{
		int k=q.front();q.pop();
		for(int i=0;i<g1[k].size();i++)
		{
			int v=g1[k][i];
			dist[v]=max(dist[v],dist[k]+p[v]);
			ind[v]--;
			if(ind[v]==0)q.push(v);
		}
	}

例題

P3387 【模板】縮點

程式碼

const int maxn=100015;
int numb,cnt;

int dist[maxn];
int dag[maxn],num[maxn];
int a[maxn];
int ind[maxn];
stack<int>st;
int p[maxn],dfn[maxn],ins[100015],low[100015];
vector<int>g[maxn],g1[maxn];
void tarjan(int x)
{

	dfn[x]=low[x]=++cnt;
	ins[x]=1;
	st.push(x);
	for(int i=0;i<g[x].size();i++)
	{
		int q=g[x][i];
		if(dfn[q]==0)
		{
			tarjan(q);
			low[x]=min(low[x],low[q]);		
		}
		else if(ins[q]==1)
		{
			low[x]=min(low[x],dfn[q]);
		}
	}
	if(dfn[x]==low[x])
	{
		numb++;
		int q;
		do
		{
			q=st.top();
			st.pop();
			ins[q]=0;
			dag[q]=numb;
			num[numb]++;
			p[numb]+=a[q];
		}
		while(q!=x);
	}

}
main(void)
{
	int n,m;
	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;
		g[u].push_back(v);
	}
	for(int i=1;i<=n;i++)
	{
		if(!dag[i])
		tarjan(i);
	}
	for(int i=1;i<=n;i++)
	for(int j=0;j<g[i].size();j++)
	{
		int u=dag[i],v=dag[g[i][j]];
		if(u!=v)
		{
			g1[u].push_back(v);
			ind[v]++;
		}
	}
	queue<int>q;
	int tot=0;
	for(int i=1;i<=numb;i++)
	{
		if(!ind[i])
		{
			q.push(i);
			dist[i]=p[i];
		}
	}
	while(!q.empty())
	{
		int k=q.front();q.pop();
		for(int i=0;i<g1[k].size();i++)
		{
			int v=g1[k][i];
			dist[v]=max(dist[v],dist[k]+p[v]);
			ind[v]--;
			if(ind[v]==0)q.push(v);
		}
	}
	int ans=0;
	for(int i=1;i<=numb;i++)
	ans=max(ans,dist[i]);
	cout<<ans;
}