【模板】缩点

洛谷p3387

思路:

算法:tarjan算法

复制代码
根据题意,我们只要找到一个路径,使得最终权重最大即可,首先,根据题目可知,如果一个点在一个环上,那么我们就将这整个环都选上,题目上允许我们能够重复走,因此,我们可以将环缩成点,将环所称点后,就可以转换成树,从没有父节点的结点开始,我们向下走,每遍历一个子结点,就将子节点更新一次,最终取结点的最大值即可
cpp 复制代码
#include<bits/stdc++.h>

using namespace std;

int n,m;

const int N=1e4+19;

const int M=1e5+10;

vector<int>vec[N];

int a[N];

int siz[N];

int cnt;

int dfn[N],low[N],tot;

int p[N];

int scc[N];

int inDegree[N];

stack<int>sta;

//tarjan模板  

void tarjan(int x){

    low[x]=dfn[x]=++tot;

    sta.push(x);

    for(auto y:vec[x]){

        if(dfn[y]==0){

            tarjan(y);

            low[x]=min(low[x],low[y]);

        }else if(!scc[y]){

            low[x]=min(low[x],dfn[y]);

        }

    }

    if(low[x]==dfn[x]){

        cnt++;

        while(1){

            int y=sta.top();

            sta.pop();

            siz[cnt]++;

            p[cnt]+=a[y];//记录每个环的总权重

            scc[y]=cnt;

            if(y==x)break;

        }

    }

}

  

struct edge{

    int from;

    int to;

}e[M];

  
  

vector<int>ve[N];

  
  
  

int ans[N];

  
  

int s;

int res=0;

//topo算法
void solve(){

    queue<int>q;

    for(int i=1;i<=cnt;i++){

        ans[i]=p[i];
 //寻找没有入读的环
        if(!inDegree[i])q.push(i);

    }

    while(q.empty()==false){

        int x=q.front();

        q.pop();

        for(auto y:ve[x]){
//从没有入度的环开始,向下遍历它出度的环
//入度的环的最大值等于指向它的环的最大值加上它自己的权重
            ans[y]=max(ans[y],p[y]+ans[x]);
//处理一个入度的边就减去一个边
            inDegree[y]--;
//如果入度的点最终没有边指向它,那么代表它就成了一个根结点,那么,就将他放入队列中
            if(inDegree[y]==0)q.push(y);

        }

    }

    for(int i=1;i<=cnt;i++){

        res=max(res,ans[i]);

    }

    cout<<res<<endl;

}

  

int main(void){

    cin>>n>>m;

    for(int i=1;i<=n;i++)cin>>a[i];

    for(int i=1;i<=m;i++){

        int a,b;

        cin>>a>>b;
//记录边的原因是为了后序我们进行环与环的入度操作时候,可以直接遍历边
        e[i].from=a;

        e[i].to=b;

        vec[a].push_back(b);

    }

    for(int i=1;i<=n;i++){

        if(!dfn[i])tarjan(i);

    }

    for(int i=1;i<=m;i++){
//记入环与环之间相连的边
        int fr=scc[e[i].from];

        int tr=scc[e[i].to];

        if(fr==tr)continue;
//记入入度的边
        inDegree[tr]++;

        ve[fr].push_back({tr});

    }

    solve();

  

}
相关推荐
U-52184F6929 分钟前
深入理解“隐式共享”与“写时复制”:从性能魔法到内存深坑
java·数据库·算法
pp起床1 小时前
Part02:基本概念以及基本要素
大数据·人工智能·算法
lzh200409191 小时前
红黑树详解
算法
迈巴赫车主1 小时前
蓝桥杯20560逃离高塔
java·开发语言·数据结构·算法·职场和发展·蓝桥杯
泯仲1 小时前
Ragent项目7种设计模式深度解析:从源码看设计模式落地实践
java·算法·设计模式·agent
dulu~dulu1 小时前
算法---寻找和为K的子数组
笔记·python·算法·leetcode
moonsea02031 小时前
【无标题】
算法
佑白雪乐2 小时前
<ACM进度212题>[2026-3-1,2026-3-26]
算法·leetcode
穿条秋裤到处跑2 小时前
每日一道leetcode(2026.03.26):等和矩阵分割 II
算法·leetcode·矩阵
平凡灵感码头2 小时前
C语言 printf 数据打印格式速查表
c语言·开发语言·算法