[Graph]Tarjan
Tarjan 算法——缩点
Tarjan算法是一种用来求解强连通分量的线性时间算法。
先介绍几个概念:
强连通:若两个点能相互到达,那么我们称这两个点强连通。
强连通图:若一个图中任意两个点能相互到达,那么这个图就叫强连通图。
强连通分量:若一个图中的某个子图中,在这个子图内的任意两点都能相互到达,那么我们称这个子图为改图的强连通分量。
DFN数组:存储每个点的DFS序。
LOW:每个点或它的子树能够到达的 最小的DFN(即最先进栈的点)。
VIS:标记某个点是否在栈中
Tarjan算法是一种基于深度优先搜索的算法,
在搜索时边处理每个节点的DFS序边入栈,
在回溯时处理点的low与dfn的关系,
若相等,则从它到栈顶的元素均在同一个强连通分量中,
(刚开始蒟蒻想了很久啊,因为是深度优先遍历啊,所以强连通分量肯定在栈中是连续的,那么在找到这个点之前那些跟此点不在同个强连通分量中肯定被弹出去了)
均弹出栈,用一个数组记录下来。
先把这个图当作一棵树进行深度优先遍历,
得到每个点的DFN,
并且在这过程中进栈,
维护LOW值。
如何求LOW?
对于当前u点到v点,
若v点之前没有被访问过,
那么tarjan这个点,
在回溯时若v点的low值小于u的low值,
那么更新u的low值。
若v被访问过并且v点的dfs序小于u的low值,
那么更新u的low值。
找环时在最后所有点均被访问后回溯时找出。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<cstdio> #include<algorithm> #include<cstring> #include<queue> #define maxn 100005 #define dian 10005 using namespace std; int vis[dian],book[dian],x[maxn],y[maxn],a[maxn],head[maxn],tot,cnt,low[maxn],color,top,ru[maxn],ans=-100000,dfn[maxn],sta[maxn],b[maxn],dis[maxn]; struct Edge{ int to,next; }e[maxn]; void addedge(int a,int b){ tot++; e[tot].to=b; e[tot].next=head[a]; head[a]=tot; ru[b]++; } void tarjan(int u){ cnt++; dfn[u]=low[u]=cnt; top++; sta[top]=u; book[u]=1; for(int i=head[u];i;i=e[i].next){ int v=e[i].to; if(!dfn[v]){ tarjan(v); low[u]=min(low[u],low[v]); } else if(book[v]) low[u]=min(low[u],dfn[v]); } if(low[u]==dfn[u]){ color++; do{ vis[sta[top]]=color; b[color]+=a[sta[top]]; book[sta[top]]=0; // printf("%d ",top); // printf("%d ",sta[top]); // top--; }while(sta[top--]!=u); // printf("\n"); } } void spfa(int u){ queue<int> q; memset(book,0,sizeof(book)); memset(dis,0,sizeof(dis)); int h=1,t=0; q.push(u); book[u]=1; dis[u]=b[u]; // printf("%d ",b[u]); while(!q.empty()){ int x=q.front(); q.pop(); book[x]=0; for(int i=head[x];i;i=e[i].next){ int v=e[i].to; if(dis[v]<dis[x]+b[v]){ dis[v]=dis[x]+b[v]; if(!book[v]){ q.push(v); book[v]=1; } } } } for(int i=1;i<=color;i++){ ans=max(ans,dis[i]); } } int main(){ int n,m,i; scanf("%d%d",&n,&m); for(i=1;i<=n;i++) scanf("%d",&a[i]); for(i=1;i<=m;i++){ scanf("%d%d",&x[i],&y[i]); addedge(x[i],y[i]); } for(i=1;i<=n;i++){ if(!dfn[i]) tarjan(i); } memset(e,0,sizeof(e)); memset(head,0,sizeof(head)); memset(ru,0,sizeof(ru)); tot=0; for(i=1;i<=m;i++){ if(vis[x[i]]!=vis[y[i]]){ addedge(vis[x[i]],vis[y[i]]); } } // printf("%d",color); for(i=1;i<=color;i++){ if(!ru[i]) spfa(i); } printf("%d",ans); return 0; }