其他分享
首页 > 其他分享> > 【NOIP2009】最优贸易

【NOIP2009】最优贸易

作者:互联网

题目链接:https://www.luogu.com.cn/problem/P1073

题目大意:给定一张有向图 , 其中每个点有一个权值 \(w\), 求出 \(1\) 到 \(n\) 的所有路径中 \(w_i - w_j\) ( \(i\) , \(j\) 为同一条路径上的两个节点 , 且 \(i\) 比 \(j\) 后遍历到 )​ 的最大值

solution

算法一:

设从 1 到 \(i\) 所有路径上节点权值的最大值为 \(g_i\) , \(i\) 到 \(n\) 所有路径上节点权值的最小值为 \(f_i\) , 可以用SPFA分别求出这两个值 , 再遍历每个节点 , 求得 \(max\left\{g_i - f_i\right\}(i \leq n)\)即为答案

code

#include<bits/stdc++.h>
using namespace std;
template <typename T> inline void read(T &FF) {
    int RR = 1; FF = 0; char CH = getchar();
    for(; !isdigit(CH); CH = getchar()) if(CH == '-') RR = -RR;
    for(; isdigit(CH); CH = getchar()) FF = FF * 10 + CH - 48;
    FF *= RR;
}
inline void file(string str) {
    freopen((str + ".in").c_str(), "r", stdin);
    freopen((str + ".out").c_str(), "w", stdout);
}
const int N = 1e6 + 10;
int n, m, now, fst[N], nxt[N], num[N], wi[N], ans;
int ni, ft[N], nt[N], nm[N];
void add(int u, int v, int k) {
    nxt[++now] = fst[u], fst[u] = now, num[now] = v;
    nt[++ni] = ft[v], ft[v] = ni, nm[ni] = u;
    if(k) {
        nxt[++now] = fst[v], fst[v] = now, num[now] = u;
        nt[++ni] = ft[u], ft[u] = ni, nm[ni] = v;
    }
}
int vis[N], ma[N], mi[N];
void Spfa_max() {
    queue<int> qi;
    qi.push(n); vis[n] = true; ma[n] = wi[n];
    while(!qi.empty()) {
        int pi = qi.front(); qi.pop(); vis[pi] = false;
        for(int i = ft[pi]; i; i = nt[i]) 
            if(max(ma[pi], wi[nm[i]]) > ma[nm[i]]) {
                ma[nm[i]] = max(ma[pi], wi[nm[i]]);
                if(!vis[nm[i]]) qi.push(nm[i]), vis[nm[i]] = true;
            }
    }
}
void Spfa_min() {
    queue<int> qi;
    memset(vis, 0, sizeof(vis));
    memset(mi, 0x3f, sizeof(mi));
    qi.push(1); vis[1] = true; mi[1] = wi[1];
    while(!qi.empty()) {
        int pi = qi.front(); qi.pop(); vis[pi] = false;
        for(int i = fst[pi]; i; i = nxt[i])
            if(min(mi[pi], wi[num[i]]) < mi[num[i]]) {
                mi[num[i]] = min(mi[pi], wi[num[i]]);
                if(!vis[num[i]]) qi.push(num[i]), vis[num[i]] = true;
            }
    }
}
int main() {
    //file("");
    int u, v, k;
    read(n), read(m);
    for(int i = 1; i <= n; i++) read(wi[i]);
    for(int i = 1; i <= m; i++)
        read(u), read(v), read(k), add(u, v, k - 1);
    Spfa_max(), Spfa_min();
    for(int i = 1; i <= n; i++)
        ans = max(ans, ma[i] - mi[i]);
    cout << ans << endl;
    return 0;
}

算法二:

建三张一模一样的分层图 , 其中每个分层图内路径长度为 0 , 相邻两张分层图中从 \(i\) 到 \(i\) 连边 , 其中一号图与二号图间 \(i\) 号节点连边的长度设为 \(-w_i\) , 意为从 \(i\) 号节点买进 , 二号图与三号图间 \(i\) 号节点连边的长度设为 \(w_i\) 意为从 \(i\) 号节点卖出 , 这样从一号图中的 1号节点到三号图中 \(n\) 号节点的最长路即为答案 , 可用SPFA求出

code

#include<bits/stdc++.h>
using namespace std;
template <typename T> inline void read(T &FF) {
    int RR = 1; FF = 0; char CH = getchar();
    for(; !isdigit(CH); CH = getchar()) if(CH == '-') RR = -RR;
    for(; isdigit(CH); CH = getchar()) FF = FF * 10 + CH - 48;
    FF *= RR;
}
inline void file(string str) {
    freopen((str + ".in").c_str(), "r", stdin);
    freopen((str + ".out").c_str(), "w", stdout);
}
const int N = 3e6 + 10;
int n, m, now, fst[N], nxt[N], num[N], wi[N], vi[N];
void add(int u, int v, int w) {
    nxt[++now] = fst[u], fst[u] = now, num[now] = v, wi[now] = w;
}
void Add_edge(int u, int v, int k) {
    add(u, v, 0), add(n + u, n + v, 0), add(n * 2 + u, n * 2 + v, 0);
    if(k == 2) add(v, u, 0), add(n + v, n + u, 0), add(n * 2 + v, n * 2 + u, 0);
}
int vis[N], lt[N];
void Spfa() {
    memset(lt, 0x80, sizeof(lt));
    queue<int> qi; qi.push(1); vis[1] = true, lt[1] = 0;
    while(!qi.empty()) {
        int pi = qi.front(); qi.pop(); vis[pi] = false;
        for(int i = fst[pi]; i; i = nxt[i])
            if(lt[pi] + wi[i] > lt[num[i]]) {
                lt[num[i]] = lt[pi] + wi[i];
                if(!vis[num[i]]) qi.push(num[i]), vis[num[i]] = true;
            }
    }
}
int main() {
    //file("");
    int u, v, k;
    read(n), read(m);
    for(int i = 1; i <= n; i++) read(vi[i]);
    for(int i = 1; i <= m; i++)
        read(u), read(v), read(k), Add_edge(u, v, k);
    for(int i = 1; i <= n; i++) add(i, i + n, -vi[i]), add(n + i, n * 2 + i, vi[i]);
    n = n * 3; Spfa();
    cout << max(lt[n], 0) << endl;
    return 0;
}

标签:CH,int,NOIP2009,vis,num,qi,贸易,最优,pi
来源: https://www.cnblogs.com/magicduck/p/12240063.html