题解【洛谷P1073】[NOIP2009]最优贸易

题面

考虑从集合的角度思考这一个问题。

首先给出我对于“分割点”的定义:即在这个城市之前买入,在这个城市之后卖出,可以在这个城市买入或卖出。

我们发现,如果我们按照哪一个城市作为分割点,那么最优解就会被包含在我们讨论的情况中。

我们设当前枚举到的分割点是 (k)(dmin_k) 表示在 (1sim k) 买入的最小值,(dmax_k) 表示在 (ksim n) 卖出的最大值。

那么我们的答案就是 (max{max_{1leq ileq n}{dmax_i-dmin_i}, 0})

注意在这一题中,使用 Dijkstra 求最短路是错误的。因为使用 Dijkstra 算法的必要条件就是 每一次取出堆的元素都不会再被更新,而在此题中,任何城市可以重复经过多次 ,所以我们取出堆的元素是有可能会被更新的。因此我们只可以使用 SPFA 算法。

我们可以建出一个反图,然后正反分别跑一遍 SPFA,求出 (dmin_i)(dmax_i) 的值。最后求出答案即可。

#include <bits/stdc++.h>

using namespace std;

const int N = 100003, M = 2000003;

int n, m;
int w[N];
int heads[N], heade[N], ver[M], nxt[M], tot;
int dmin[N], dmax[N];
int vis[N];

inline void add(int head[], int u, int v)
{
    ver[++tot] = v, nxt[tot] = head[u], head[u] = tot;
}

inline void SPFA(int head[], int dist[], int ty)
{
    queue <int> q;
    if (ty == 0) 
    {
        memset(dist, 0x3f, sizeof dmin);
        dist[1] = w[1];
        q.push(1);
    }
    else 
    {
        memset(dist, 0xcf, sizeof dmax);
        dist[n] = w[n];
        q.push(n);
    }
    memset(vis, 0, sizeof vis);
    while (!q.empty())
    {
        int u = q.front(); q.pop();
        vis[u] = 0;
        for (int i = head[u]; i; i = nxt[i])
        {
            int v = ver[i];
            if (ty == 0)
            {    
                if (dist[v] > min(dist[u], w[v]))
                {
                    dist[v] = min(dist[u], w[v]);
                    if (!vis[v])
                        vis[v] = 1, q.push(v);
                }
            }
            else
            {
                if (dist[v] < max(dist[u], w[v]))
                {
                    dist[v] = max(dist[u], w[v]);
                    if (!vis[v])
                        vis[v] = 1, q.push(v);
                }
            }
        }
    }
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i+=1) scanf("%d", &w[i]);
    for (int i = 1; i <= m; i+=1)
    {
        int u, v, w;
        scanf("%d%d%d", &u, &v, &w);
        add(heads, u, v), add(heade, v, u); //建反图
        if (w == 2) add(heads, v, u), add(heade, u, v); //双向边
    }
    SPFA(heads, dmin, 0); //正着求出 dmin
    SPFA(heade, dmax, 1); //反着求出 dmax
    int ans = 0;
    for (int i = 1; i <= n; i+=1)
        ans = max(ans, dmax[i] - dmin[i]); //取最大值
    printf("%d
", ans);
    return 0;
}
原文地址:https://www.cnblogs.com/xsl19/p/12382795.html