tarjan强联通分量和缩点

https://www.cnblogs.com/dx123/p/16320476.html
https://www.cnblogs.com/dx123/p/16320478.html

Luogu P3387 【模板】缩点


#include<bits/stdc++.h>
using namespace std;
const int N=10010;
int dfn[N],low[N],tot;
int stk[N],instk[N],top;
int scc[N],siz[N],cnt;
vector<int> e[N], ne[N];// e 原始图 , ne 新图
int n,m,u,v;
int w[N], nw[N];//w 是原始的权值,nw 是新的权值
int dp[N];

void tarjan(int x){
    dfn[x] = low[x] = ++tot;
    stk[++top] = x;
    instk[x] = 1;
    for (int y : e[x]) {
        if (!dfn[y]) { // y is not visited
            tarjan(y);
            low[x] = min(low[x], low[y]); // update low when returning to x
        } else if (instk[y]) { // y is visited and in stack
            low[x] = min(low[x], dfn[y]); // update low when in x
        }
    }
    if (dfn[x] == low[x]) { // x is the root of SCC
        int y; ++cnt;
        do {
            y = stk[top--];
            instk[y] = 0;
            scc[y] = cnt; // SCC number
            ++siz[cnt];   // SCC size
        } while (y != x);
    }
}
int main()
{
    cin >> n >> m;
    for(int i = 1; i <= n; i++) {
        cin >> w[i];
    }
    while(m--) {
        cin >> u >> v;
        e[u].push_back(v);
    }
    for(int i=1; i<=n; i++) {
        if(!dfn[i]) {
            tarjan(i);
        }
    }
    // 缩点,建新图
    for(int i=1; i<=n; i++) {
        nw[scc[i]] += w[i]; // 将原始权值累加到新的权值上
        for(auto j : e[i]) {
            if(scc[i] != scc[j]) { // 如果 i 和 j 不在同一个强连通分量中
                ne[scc[i]].push_back(scc[j]); // 在新图中添加边
            }
        }
    }

    // * 注意到强联通分量从大到小编号就已经是拓扑排序过的顺序,所以可以直接dp
    for(int i=cnt; i>=1; i--) {
        if(dp[i] == 0) //起点
            dp[i] = nw[i]; 
        
        for(auto j : ne[i]) {
            dp[j] = max(dp[j], dp[i] + nw[j]); // 更新 dp 数组
        }     
    }

    int ans = 0;
    for(int i=1; i<=cnt; i++) {
        ans = max(ans, dp[i]); // 找到最大的权值
    }
    cout << ans << endl; // 输出结果
    
    return 0;
}

image

posted @ 2025-05-09 15:25  katago  阅读(14)  评论(0)    收藏  举报