JavaScript簡介
阿新 • • 發佈:2021-07-08
Tarjan
作用:
- 求強連通分量(求環)
- 求割點(同上)
- 縮點,將有向圖變成有向無環圖(進行topsort)
實現:
void tarjan(int x){ low[x]=dfn[x]=++cnt; sta[++top]=x;//在棧中,表示在同一個強連通分量 vis[x]=1; for(int i=head[x];i;i=nxt[i]){ int y=ver[i]; if(!dfn[y]){ tarjan(y); low[x]=min(low[x],low[y]); } else if(vis[y]) low[x]=min(low[x],dfn[y]);//搜尋到的最小棧中節點dfs序 } if(dfn[x]==low[x]){ int y; while(y=sta[top--]){//縮點 sd[y]=x; vis[y]=0; if(x==y) break; val[x]+=val[y]; } } }
其中,\(dfn[x]\) 為 \(x\) 的 \(dfs\) 序,而 \(low[x]\) 為在該強連通分量中所能找到的最小的 \(dfs\) 序的值。
如果 \(dfn[x]==low[x]\) ,那麼這個點就是割點,此時棧中從 \(sta[top] ——> x\) 就是這個強連通分量的點。
又因為加入棧的順序是按邊加入的,所以我們也能記錄強連通分量的邊,從而得到圖中的環。
我們在縮點之後整個圖就變成 \(DAG\) ,就能進行拓撲或者算權值等操作。
例題:
P3387 【模板】縮點
這題是模板題。
我們首先需要把所有點都縮成一個點,然後進行 \(dp\)。
我們怎麼進行 \(dp\)
程式碼:
//P3387 【模板】縮點 #include<bits/stdc++.h> using namespace std; const int N=1e5+5; int n,m,val[N],top; int nxt[N],ver[N],tot,head[N],from[N]; int dfn[N],low[N],cnt; int sta[N],vis[N];//棧表示此時是否有父子關係 int sd[N];//把環上所有點都彙總到一個點 int deg[N],dis[N]; struct node{ int nxt,ver,edge,head,from; }t[N]; void add(int x,int y){ ver[++tot]=y;from[tot]=x;nxt[tot]=head[x];head[x]=tot; } void tarjan(int x){ low[x]=dfn[x]=++cnt; sta[++top]=x;//在棧中,表示在同一個強連通分量 vis[x]=1; for(int i=head[x];i;i=nxt[i]){ int y=ver[i]; if(!dfn[y]){ tarjan(y); low[x]=min(low[x],low[y]); } else if(vis[y]) low[x]=min(low[x],dfn[y]);//搜尋到的最小棧中節點dfs序 } if(dfn[x]==low[x]){ int y; while(y=sta[top--]){//縮點 sd[y]=x; vis[y]=0; if(x==y) break; val[x]+=val[y]; } } } int tot1=0; void add1(int x,int y,int z){ t[++tot1].ver=y; t[tot1].nxt=t[x].head; t[tot1].edge=z; t[x].head=tot1; deg[y]++; } void topsort(){ queue<int> q; for(int i=1;i<=n;i++) if(sd[i]==i&&!deg[i]){ q.push(i); dis[i]=val[i]; } while(!q.empty()){ int x=q.front() ; q.pop(); for(int i=t[x].head;i;i=t[i].nxt){ int y=t[i].ver; dis[y]=max(dis[y],dis[x]+val[y]); deg[y]--; if(deg[y]==0) q.push(y); } } } int main() { cin>>n>>m; for(int i=1;i<=n;i++) scanf("%d",&val[i]); for(int i=1,x,y;i<=m;i++){ scanf("%d%d",&x,&y); add(x,y); } for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); for(int i=1;i<=m;i++){ int x=sd[from[i]],y=sd[ver[i]]; //沒有縮成一個點 if(x!=y) add1(x,y,val[x]); } topsort(); int ans=0; for(int i=1;i<=n;i++) ans=max(ans,dis[i]); cout<<ans<<endl; system("pause"); return 0; }