P3387 【模板】缩点 洛谷 java题解 连通图+拓扑排序

传送门:

P3387 【模板】缩点 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)icon-default.png?t=M85Bhttps://www.luogu.com.cn/problem/P3387


问题分析

        首先为了节省空间和提高效率,使用链式前向星来存储图,图论基操了属于。由于是有向图,所以是强连通图的题型。tarjin算法走一遍后得到一个新的图,这个图不存在强连通分量,形成一个有向无环图,显然可以用拓扑排序。


代码:

import java.io.*;
import java.util.*;

public class Main {
    static int maxn=10001,maxm=100001;     //n和m的最大值
    static int n,m,cnt,num,idx,top;        //后面用到的所有全局静态变量
    static int[] eu=new int[maxm],ev=new int[maxm];
    static int[] vv=new int[maxm],to=new int[maxm];//vv,to,he是链式前向星标配
    static int[] he=new int[maxn],stk=new int[maxn];//stk模拟栈
    static int[] dfn=new int[maxn],low=new int[maxn];//dfn,low,scc,stk是tarjin标配
    static int[] scc=new int[maxn],ind=new int[maxn];//ind记录入度
    static int[] ga=new int[maxn],dis=new int[maxn];//ga记录每一个点的点权(tarjin后新的图)
    static int[] arr=new int[maxn];  //arr记录每一个点的点权(输入图)
    public static void main(String[] args) throws IOException {
        //输入部分
        n=nextInt();m=nextInt();
        for(int i=1;i<=n;i++) arr[i]=nextInt();
        for(int i=1;i<=m;i++){
            eu[i]=nextInt();
            ev[i]=nextInt();
            addEdge(eu[i],ev[i]);
        }
        for(int i=1;i<=n;i++)
            tarjin(i);
        Arrays.fill(vv,0);
        Arrays.fill(to,0);
        Arrays.fill(he,0);cnt=0;
        for(int i=1;i<=m;i++){
            if(scc[eu[i]]!=scc[ev[i]]){
                ind[scc[ev[i]]]++;
                addEdge(scc[eu[i]],scc[ev[i]]);
            }
        }
        int ans = topo();
        out.println(ans);
        out.close();        //close不能忘
    }

    //标准topo排序
    static int topo(){
        int ans=0;
        Queue<Integer> q = new LinkedList<>();
        for(int i=1;i<=idx;i++){
            if(ind[i]==0){
                dis[i]=ga[i];
                q.offer(i);
            }
        }
        while(!q.isEmpty()){
            Integer u = q.poll();
            for(int i=he[u];i>0;i=to[i]){
                int v=vv[i];
                ind[v]--;
                if(ind[v]==0) q.offer(v);
                dis[v]=Math.max(dis[v],dis[u]+ga[v]);
                ans=Math.max(ans,dis[v]);
            }
        }
        for(int i=1;i<=idx;i++)
            ans=Math.max(ans,dis[i]);
        return ans;
    }

    static void tarjin(int root){
        if(dfn[root]>0) return;    //若该点访问过了,return
        dfn[root]=low[root]=++num; //更新dfn和low
        stk[++top]=root;           //更新栈
        for(int i=he[root];i>0;i=to[i]){
            int v=vv[i];
            if(dfn[v]==0){        //如果该点没有访问过
                tarjin(v);        //那么递归下去
                low[root]=Math.min(low[v],low[root]); 
            } else if(scc[v]==0) {
                low[root]=Math.min(dfn[v],low[root]);
            }
        }
        //若dfn和low一致,说明栈中root上面所有点共同组成一个强连通子图
        if(dfn[root]==low[root]){
            idx++;
            for(;;){
                //将这些点合并,在新图中成为一个点
                int x=stk[top--];    //出栈
                scc[x]=idx;         //scc对应新图数据
                ga[idx]+=arr[x];    //更新新图点的点权
                if(x==root) break;
            }
        }
    }

    public static void addEdge(int u,int v){
        vv[++cnt]=v;
        to[cnt]=he[u];
        he[u]=cnt;
    }

    //以下为java魔法快速输入
    static StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
    static PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));

    public static int nextInt() throws IOException{
        in.nextToken();
        return (int)in.nval;
    }

    //在这里并没有用到,多写无碍
    public static String nextString() throws IOException {
        in.nextToken();
        return in.sval;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

玛卡左家陇分卡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值