1. 程式人生 > >tarjan演算法(縮點)

tarjan演算法(縮點)

tarjan可以求強連通分量,在強連通分量的基礎上,可以加一些操作來縮點。

(我覺得此處應該有個圖,即使不太用qwq)

比如有一張這個圖(懶得不想標號系列)

它的強連通分量的情況大概是這樣子(忽視無意間甩過去的那一筆)

於是把它的強連通分量縮成點,就得到了這個東西

(小精簡變得真東西)

現在已經知道了縮點是什麼東西,那麼如何縮呢?

只需要記錄一個belong陣列,belong[x]表示x所屬的強連通分量的分量主

然後我們遍歷原圖,如果原圖中有一條邊,其兩個端點(u,v)的belong值不同,就在新圖中給belong[u],belong[v]連邊。

然後你就過了。

一道模板題

這道題的思路,就是縮點+拓撲鬆弛(貌似SPFA也可行)

既然新圖中的一些點是由強連通分量縮來的,點內各點必定能各自到達。所以只需要tarjan時順手改一下點權(程式碼裡是p[]),最後用d[]做鬆弛,跑一邊就好了。

因為縮過點,這個複雜度顯然是比較優秀的。

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<queue>
#include<algorithm>
#define maxn 10005
#define maxm 100005
using namespace std;
int n,m;
int head[maxn],h[maxn],tot=0;//建圖用的陣列 
int dfn[maxn],low[maxn],v[maxn],sta[maxn],id=0;//tarjan用的陣列 
int bel[maxn],p[maxn],d[maxn],in[maxn],tp=0;//拓撲用的陣列 

struct node{
	int x,y,nxt;
}e[maxm],ed[maxm];

inline void ad(int x,int y)
{
	++tot;
	e[tot].nxt=head[x];e[tot].x=x;e[tot].y=y;head[x]=tot;
}

inline void tarjan(int x)
{
	low[x]=dfn[x]=++id;
	sta[++tp]=x;v[x]=1;
	for(int i=head[x];i;i=e[i].nxt){
		int y=e[i].y;
		if(!dfn[y]){
			tarjan(y);
			low[x]=min(low[x],low[y]);
		}
		else if(v[y]){
			low[x]=min(low[x],dfn[y]);
		}
	}
	if(dfn[x]==low[x]){
		int y;
		while(y=sta[tp--])
		{
			bel[y]=x;//在這個強連通分量裡的點都歸順於x 
			v[y]=0;
			if(x==y)break;
			p[x]+=p[y];
		}
	}
}

inline int tps()
{
	queue<int> q;
	tot=0;
	for(int i=1;i<=n;++i)
	if(bel[i]==i&&!in[i])//是團隊主且入度為0 
	{
		q.push(i);
		d[i]=p[i];
	}
	while(!q.empty())
	{
		int x=q.front();q.pop();
		for(int i=h[x];i;i=ed[i].nxt)
		{
			int y=ed[i].y;
			d[y]=max(d[y],d[x]+p[y]);
			--in[y];
			if(!in[y])q.push(y);
		}
	}
	int ans=0;
	for(int i=1;i<=n;++i)
	ans=max(ans,d[i]);
	return ans;
}

int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i)
	scanf("%d",&p[i]);
	int x,y;
	for(int i=1;i<=m;++i)
	{
		scanf("%d%d",&x,&y);
		ad(x,y);
	}
	for(int i=1;i<=n;++i)
	if(!dfn[i])tarjan(i);
	int sum=0;
	for(int i=1;i<=m;++i)
	{
		int x=bel[e[i].x],y=bel[e[i].y];
		if(x!=y)
		{
			ed[++sum].nxt=h[x];
			ed[sum].y=y;
			ed[sum].x=x;
			h[x]=sum;
			++in[y];
		}
	}//構建新圖 
	printf("%d",tps());
	return 0;
}

完結撒花~

明天還要上學啊qwq