题解:P1073 [NOIP 2009 提高组] 最优贸易

题目传送门

绝世好题
让我学会了分层图的真正用法
以前都只会自作聪明地分两层:
美其名曰【正图】【反图】
现在知道了还有这种神奇的建图方法!


理解题意:

有向图
给定起点终点
求路径上两点买卖东西最大收益

思路:

考虑建分层图

所以此时问题从二维图s->t
转化为了
三维图(分层图)中的s->t3

我们对于层之间的边怎么操作呢?
其实每层对应着不同的状态

  • 第一层对应没有买入
  • 第二层对应已经买入
  • 第三层对应已经卖出
    从第一层到第二层的边就是 -v ,负的价格,表示此时买入
    从第二层到第三层的边就是 v ,正的价格,表示此事卖出
    而我们让同一层之间的边的权值为 0 , 仅仅只检测连通性就好了
    建图建完(最繁重的工作)那么我们只需要跑一边SPFA求出第一层的起点到第三层的终点
    (经历了一次买入和一次卖出的路径)
    就解决了这个问题

\(\mathbb {CODE}\)

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 10;

int n, m;
int dis[N * 3], vis[N * 3];
vector<pair<int, int> > e[N * 3];

void add(int u, int v)
{
    e[u].push_back({v, 0});
    e[u + n].push_back({v + n, 0});
    e[u + n + n].push_back({v + n + n, 0});
}

void solve()
{
    memset(dis, -0x3f, sizeof dis);
    queue<int> q;
    q.push(1);
    vis[1] = 1;
    dis[1] = 0;
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        vis[u] = 0;
        for (auto t : e[u]) {
            int v = t.first, w = t.second;
            if (dis[v] < dis[u] + w) {
                dis[v] = dis[u] + w;
                if (!vis[v]) {
                    q.push(v);
                    vis[v] = 1;
                }
            }
        }
    }
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        int price;
        cin >> price;
        e[i].push_back({i + n, -price});
        e[i + n].push_back({i + n + n, price});
    }
    for (int i = 1; i <= m; i++) {
        int u, v, id;
        cin >> u >> v >> id;
        add(u, v);
        if (id ^ 1)
            add(v, u);
    }
    solve();
    cout << dis[n * 3] << endl;
    return 0;
}

还有一道很水的紫题

posted @ 2025-10-19 21:24  Noivelist  阅读(13)  评论(0)    收藏  举报