P1073 [NOIP2009 提高组] 最优贸易 分层图

//题意:给出有向图,有环(SCC),每个节点有一个 商品值 ,小明想从1点走向n点,同时想要进行一次贸易,即从路线上某个点买入商品, 又在某个节点卖出, 询问最大收益是多少(如果收益为负数那么输出0)
//思路:其实环对做题造成的影响,本质上可以归结于对单线形逻辑结构的破坏(有点dp中无后效性的意味)
//      https://blog.csdn.net/You_are_hanson/article/details/125932502
//      这篇博客讲得很好,其实对于消除环带来的影响,在这题中分层图也能实现
//      因为将 未操作 买进 卖出 这三种状态分开的话,层内的点是无法对层内的点做出贡献的,因为只有实现状态的跨越才能使得值变化
//      所以层内的环是无意义的,我们只需要保证层内一个点只经过一次就不用担心环的问题
//      同时分成三层后,三层之间是一个单线性无环的逻辑关系,所以这就变成了用spafa跑最长路了
//
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n, m;
vector<pair<int, int>> mp[3 * N];
int dis[3 * N], cnt, stp;
bool b[3 * N];
queue<int> lis;
void bellman_ford(int a) {
    memset(b, false, sizeof(b));
    for (int i = 1; i <= n; i++) dis[i] = dis[i + N] = dis[i + 2 * N] = INT_MIN;//这里很妙,因为层内的所有边权为0,所以将每个点的权值初始化为极小值可以保证至多经过层内的点一次
    
    dis[a] = 0;
    lis.push(a);
    b[a] = true;
    while (!lis.empty()) {
        int x = lis.front();
        lis.pop();
        b[x] = false;
        for (auto y : mp[x]) {
            if (dis[x] + y.second > dis[y.first]) {
                dis[y.first] = dis[x] + y.second;
                if (!b[y.first])
                    lis.push(y.first);
                b[y.first] = true;
            }
        }
    }
}
int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        int a; cin >> a;
        mp[i].push_back({ i + N,-a });
        mp[i + N].push_back({ i + 2 * N,a });
    }//建层与层之间的边
    for (int i = 1; i <= m; i++) {
        int a, b, od; cin >> a >> b >> od;
        mp[a].push_back({ b,0 });
        mp[a + N].push_back({ b + N,0 });
        mp[a + 2 * N].push_back({ b + 2 * N,0 });
        if (od == 2) {
            mp[b].push_back({ a,0 });
            mp[b + N].push_back({ a + N,0 });
            mp[b + 2 * N].push_back({ a + 2 * N,0 });
        }
    }//建层内边
    bellman_ford(1);
    cout << ((dis[n + 2 * N] > 0) ? dis[n + 2 * N] : 0);
    return 0;
}

 

posted @ 2023-01-08 18:22  Aacaod  阅读(13)  评论(0编辑  收藏  举报