强连通分量——tarjan

本文详细解析了洛谷P2812校园网络和P3387缩点两道算法题目,通过Tarjan算法进行缩点处理,并分别采用SPFA算法和拓扑排序求解最长路径问题。

洛谷 P2812 校园网络
洛谷 P3387 缩点

洛谷P2812 校园网络

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
struct arr{
    int nd,nx;
}bot[1100000],d[1100000];
int head[20000],h[20000],stack[20000],low[20000],dfn[20000],bl[20000];
int rd[20000],cd[20000],tot,cnt,pos,top,scc,size[20000],n;
bool instack[20000];
inline int read(){
    int x=0,w=1;char ch=0;
    while(ch!='-'&&(ch<'0'||ch>'9'))  ch=getchar();
    if(ch=='-') w=-1,ch=getchar();
    while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch-48),ch=getchar();
    return x*w; 
}
inline void add(int a,int b){bot[++cnt].nd=b;bot[cnt].nx=head[a];head[a]=cnt;}
inline void ad1(int a,int b){d[++tot].nd=b;d[tot].nx=h[a];h[a]=tot;rd[b]++;cd[a]++;}
inline void tarjan(int u){
    dfn[u]=low[u]=++pos;
    stack[++top]=u;instack[u]=true;
    for(register int i=head[u];i;i=bot[i].nx) {
        int v=bot[i].nd;
        if(instack[v]) low[u]=min(dfn[v],low[u]);
        if(dfn[v]==0) {tarjan(v);low[u]=min(low[v],low[u]);}
    }
    if(low[u]==dfn[u]){
        ++scc;int t=0;
        while(u!=t)
{ t=stack[top--];instack[t]=false;bl[t]=scc;size[scc]++; }
    }
}
int main(){
    n=read();
    for(register int i=1;i<=n;++i) {int x=read();while(x){ add(i,x); x=read(); }}
    for(register int i=1;i<=n;++i) if(!dfn[i])tarjan(i);
    tot=0;
    for(register int i=1;i<=n;++i)
        for(register int j=head[i];j;j=bot[j].nx){
            int v=bot[j].nd;
            if(bl[i]!=bl[v]) ad1(bl[i],bl[v]);
        }
    int ans1=0,ans2=0;
    for(register int i=1;i<=scc;++i){//统计一下缩点之后的入度和出度
        if(!rd[i]) ans1++;
        if(!cd[i]) ans2++;
    }
    if(scc==1) printf("1\n0");
    else printf("%d\n%d",ans1,max(ans1,ans2));
} 

洛谷 P3387 缩点

一道模板题,就直接贴代码了。

下面这份代码是先tarjan缩点,再建立一个新图,然后加上SPFA

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
struct arr{
    int nd,nx;
}bot[1100000],d[1100000];
int head[100000],h[100000],stack[100000],low[100000],dfn[100000],bl[100000];
int tot,cnt,pos,top,scc,m,n;
int c[100000],w[100000];
long long dist[100000],f[100000];
long long ans;
bool instack[100000];
inline int read(){
    int x=0,w=1;char ch=0;
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') w=-1,ch=getchar();
    while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch-48),ch=getchar();
    return x*w;
}
inline void add(int a,int b){bot[++cnt].nd=b;bot[cnt].nx=head[a];head[a]=cnt;}
inline void ad1(int a,int b){d[++tot].nd=b;d[tot].nx=h[a];h[a]=tot;}
inline void tarjan(int u){ 
    dfn[u]=low[u]=++pos;
    stack[++top]=u;instack[u]=true;
    for(register int i=head[u];i;i=bot[i].nx){
        int v=bot[i].nd;
        if(!dfn[v]) { tarjan(v);low[u]=min(low[v],low[u]);}
        if(instack[v]) low[u]=min(dfn[v],low[u]);
    } 
    if(dfn[u]==low[u]){
        int t=0;++scc;
        while(u!=t){ t=stack[top--];instack[t]=false;bl[t]=scc;c[scc]+=w[t];}
    }
}
inline void SPFA(int s){
    deque<int>q;
    for(int i=1;i<=n;i++) dist[i]=0,f[i]=0;
    dist[s]=c[s];f[s]=1;
    q.push_back(s);
    while(!q.empty()){
        int now=q.front();
        q.pop_front();
        f[now]=0;
           for(register int i=h[now];i;i=d[i].nx){
             int v=d[i].nd;
             if(dist[v]<dist[now]+c[v]){
                dist[v]=dist[now]+c[v];
                if(!f[v]){
                    f[v]=1;
                    if(q.empty()||dist[v]>dist[q.front()]) q.push_back(v);
                    else q.push_front(v);
                }
            }
          }
    }
    for(register int i=1;i<=scc;++i)  if(ans<dist[i]) ans=dist[i];
}
int main(){
    n=read(); m=read();
    for(register int i=1;i<=n;++i) w[i]=read();
    for(register int i=1;i<=m;++i) { int u=read(),v=read(); add(u,v); }
    for(register int i=1;i<=n;++i) if(!dfn[i]) tarjan(i);
    for(register int i=1;i<=n;++i)
        for(register int j=head[i];j;j=bot[j].nx) {
            int v=bot[j].nd;
            if(bl[i]!=bl[v]) 
                ad1(bl[i],bl[v]);
        }
    for(register int i=1;i<=scc;++i)  SPFA(i);
    //对于缩点后新建的图跑SPFA
    printf("%d\n",ans);
}

下面这个代码是先tarjan缩点,建立一个新图,然后加上拓扑排序求最长路。
(本蒟一开始做的时候只有40分,后面请了机房某xxy大佬帮我改了下才过的)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
struct arr{
    int nd,nx;
}bot[210000],d[210000];
int head[20000],a[20000],h[20000];
int rd[20000],cd[20000],dis[20000],topo[30000];
int low[20000],dfn[20000],stack[20000],instack[20000],bl[20000],size[20000],c[20000];
int n,m,cnt,tot,tt,top,pos,scc,ans;
inline int read(){
    int x=0,w=1;char ch=0;
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') w=-1,ch=getchar();
    while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch-48),ch=getchar();
    return x*w;
}
inline void add(int a,int b) { bot[++cnt].nd=b; bot[cnt].nx=head[a]; head[a]=cnt; }
inline void ad1(int a,int b) { d[++tot].nd=b; d[tot].nx=h[a]; h[a]=tot;rd[b]++;cd[a]++;}
inline void tarjan(int u){
    dfn[u]=low[u]=++pos;
    stack[++top]=u; instack[u]=true;
    for(register int i=head[u];i;i=bot[i].nx) {
        int v=bot[i].nd;
        if(!dfn[v]) { tarjan(v);low[u]=min(low[u],low[v]); }
        if(instack[v]) {low[u]=min(dfn[v],low[u]);}
    } 
    if(dfn[u]==low[u]) {
        int t=0;++scc;
        while(t!=u) { t=stack[top--]; instack[t]=false; bl[t]=scc; size[scc]++; c[scc]+=a[t];}
    }
}
inline void tuopu(){
    queue<int> q;
    for(register int i=1;i<=scc;++i) if(!rd[i]) q.push(i);
    while(!q.empty()) {
        int u=q.front();q.pop();
        topo[++tt]=u;
        for(register int i=h[u];i;i=d[i].nx) {
            int v=d[i].nd;--rd[v];
            if(!rd[v]) q.push(v);
        }
    }
    for(register int i=1;i<=scc;++i) dis[i]=c[i];
    int ans=0;
    for(int k=1;k<=tt;++k) {
        int u=topo[k];
        for(register int i=h[u];i;i=d[i].nx) {
            int v=d[i].nd;
            dis[v]=max(dis[v],dis[u]+c[v]);
        }
    }
    for(register int i=1;i<=scc;++i) ans=max(ans,dis[i]);
    printf("%d\n",ans);
}
int main(){
    n=read();m=read();
    for(register int i=1;i<=n;++i) a[i]=read();
    for(register int i=1;i<=m;++i) {
        int u=read(),v=read();
        add(u,v);
    }
    for(register int i=1;i<=n;++i)if(!dfn[i]) tarjan(i);
    for(register int i=1;i<=n;++i)
        for(register int j=head[i];j;j=bot[j].nx) {
            int v=bot[j].nd;
            if(bl[i]!=bl[v]) { ad1(bl[i],bl[v]); }//建立新图的时候要注意加入的点是bl[i]和bl[v],不是i和v,错了好几次
        }
    tuopu();
    return 0;
} 
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值