Tarjan缩点
从上周五开始学缩点,今天终于把代码敲过了,再次做一个总结(有好多问题需要总结)。
概念:
缩点:在一张有向图中,将图中的强连通分量缩成一个点。
连通分量:一个点集,是的点集中的点两两之间可以互相抵达。
强连通分量:这个点集中无法再加入一个新的点。
思路:
1.从一个未搜过的点开始,将这个点标记为正在搜的点,并用另一数组记录这个点为第几个被搜到的点(dfn),把这个点放入栈中。
2.搜这个点能到达的所有的点,并用一个数组记录它能到的所有的点中dfn最小的一个(low)(能到的所有的点包括未搜的和正在搜的)。
3.在搜完后判断如果这个点的dfn和low相等说明从栈顶到这个元素属于同一个强连通分量,并把它们从这个栈中删掉,将这些点标记为搜完的点。
我在学缩点的期间遇到的一些问题:
1.
从点1开始,先查2,再查3,再查4,又查到1,返回到1,接着查5,返回来dfn[1]是等于low[1]的,所以这是一个强连通分量,但是点1,2,3,4是一个强连通分量,不包括5。
解释:判断点1前就已经判断了点5,而且已经将点5自己判断为一个强连通分量,出栈了,所以记录时会将点1,2,3,4记录为一个强连通分量,点5记录为一个强连通分量。
2.
我原来的时候一直在一个点搜完后将它标记为搜完,而不是在它出栈时标记,就一直错。
解释:根据上面这个图,点1,2,3,4显然是一个强连通分量,但是如果在一个点搜完后就立刻将它标记为搜完,点4就不会搜点3了,这样就会将点4自己标记为一个强连通分量所以要在它出栈时再将它标记为搜完。
下面是代码:
点击查看代码
#include<iostream>
using namespace std;
int n,m;
int a[10010];
int head[10010],tot;
int shead[10010],stot;
struct node{
int to;
int nxt;
}edge[100010],sedge[100010];
void build(int u,int v){
edge[++tot].to=v;
edge[tot].nxt=head[u];
head[u]=tot;
}
void addedge(int u,int v){
sedge[++stot].to=v;
sedge[stot].nxt=shead[u];
shead[u]=stot;
}
int dfn[10010],low[10010],cnt;
int zhen[100010],top;
int dian[10010],shu;
int dui[10010];
int ru[10010];
int vis[10010];
void dfs(int u){
low[u]=dfn[u]=++cnt;
zhen[++top]=u;
vis[u]=1;
for(int i=head[u];i;i=edge[i].nxt){
int v=edge[i].to;
if(dfn[v]==0){
dfs(v);
low[u]=min(low[v],low[u]);
}
else if(vis[v]==1)low[u]=min(low[u],low[v]);
}
if(low[u]==dfn[u]){
shu++;
while(zhen[top]!=u){
vis[zhen[top]]=0;
dian[shu]+=a[zhen[top]];
dui[zhen[top]]=shu;
top--;
}
top--;
dui[u]=shu;
dian[shu]+=a[u];
vis[u]=0;
}
}
int maxn;
int f[10010];
void tuopu(int u){
for(int i=shead[u];i;i=sedge[i].nxt){
int v=sedge[i].to;
if(f[u]+dian[v]>f[v]){
f[v]=f[u]+dian[v];
maxn=max(maxn,f[v]);
tuopu(v);
}
}
}
int main(){
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;
build(u,v);
}
for(int i=1;i<=n;i++){
if(dfn[i]==0){
dfs(i);
}
}
for(int i=1;i<=n;i++){
for(int j=head[i];j;j=edge[j].nxt){
int v=edge[j].to;
if(dui[v]!=dui[i]){
addedge(dui[i],dui[v]);
ru[dui[v]]++;
}
}
}
for(int i=1;i<=shu;i++){
if(ru[i]==0){
f[i]=dian[i];
maxn=max(f[i],maxn);
tuopu(i);
}
}
cout<<maxn;
return 0;
}