P1073 [NOIP2009 提高组] 最优贸易 强联通分量+缩点

//题意:给出有向图,有环(SCC),每个节点有一个 商品值 ,小明想从1点走向n点,同时想要进行一次贸易,即从路线上某个点买入商品, 又在某个节点卖出, 询问最大收益是多少(如果收益为负数那么输出0)
//思路:有环第一时间想到tarjan缩点,重建一幅无环有向图
//      因为我想取得最大收益,所以在到达一个点时,我应该用路径上的最小价进货,同时看在这点卖出是否能取得最大值,是一个dp问题(用前点更新后点)
//      注释内是用的dfs序跑dp,但是会T掉几个点,具体的情况实在无法想象出来
//      所以为了保险起见,跑dp这种需要前提条件来更新后面条件的有序结构,要使用拓扑序
//      另外缩点会有重边问题,本题无影响,但是其他题要视具体情况考虑
//
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
vector<int> mp[N], mp2[2 * N];
int n, m, wt[N];
int dfn[N], low[N], ins[N], bel[N], idx, cnt;//dfn序,low值,是否存在,哪个强联通分量(缩点用)
stack<int> stk;
int dp[N];
map<pair<int, int>, bool> ct;
struct point {
    int in, out;
}fmp[2 * N];
void dfs(int u) {
    dfn[u] = low[u] = ++idx;
    ins[u] = true;
    stk.push(u);

    for (auto v : mp[u]) {
        if (!dfn[v]) dfs(v);
        if (ins[v]) low[u] = min(low[u], low[v]);
    }

    if (dfn[u] == low[u]) {
        ++cnt;
        int maxn = -1e9, minn = 1e9;
        while (true) {
            int v = stk.top();
            maxn = max(maxn, wt[v]);
            minn = min(minn, wt[v]);
            ins[v] = false;
            bel[v] = cnt;
            stk.pop();
            if (u == v) break;
        }
        fmp[cnt].in = minn;
        fmp[cnt].out = maxn;
    }
}
/*void dfs2(int x) {
    for (auto y : mp2[x]) {
        if (fmp[y].in > fmp[x].in) fmp[y].in = fmp[x].in;
        dp[y] = max(dp[y], max(dp[x], fmp[y].out - fmp[y].in));
        dfs2(y);
    }
}*/
int rd[2 * N];
void solve() {
    queue<int> st;
    st.push(bel[1]);
    while (!st.empty()) {
        int x = st.front();
        st.pop();
        for (auto y : mp2[x]) {
            if (fmp[y].in > fmp[x].in) fmp[y].in = fmp[x].in;
            dp[y] = max(dp[y], max(dp[x], fmp[y].out - fmp[y].in));
            rd[y]--;
            if (rd[y] == 0) st.push(y);
        }
    }
}
int main() {
    cin >> n >> m;
    cnt = n;
    for (int i = 1; i <= n; i++) cin >> wt[i];
    for (int i = 1; i <= m; i++) {
        int a, b, od; cin >> a >> b >> od;
        mp[a].push_back(b);
        if (od == 2) mp[b].push_back(a);
    }
    dfs(1);

    for (int i = 1; i <= n; i++) {
        for (auto x : mp[i]) {
            if (bel[x] == bel[i]) continue;
            if (ct[{bel[x], bel[i]}] || ct[{bel[i], bel[x]}]) continue;
            ct[{bel[x], bel[i]}] = ct[{bel[i], bel[x]}] = true;
            rd[bel[x]]++;
            mp2[bel[i]].push_back(bel[x]);
        }
    }
    
    //dfs2(bel[1]);
    solve();
    cout << dp[bel[n]];
    return 0;
}

 

posted @ 2023-01-08 16:20  Aacaod  阅读(26)  评论(0编辑  收藏  举报