tarjan演算法(縮點)
阿新 • • 發佈:2019-02-06
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