Noip 2009 最优贸易 - BFS

30 篇文章 0 订阅

题目要求一个最大差值,然而买入必须要在卖出之前,也就是说某些单向边会影响答案

设dis1[i]为从起点到i点的最低价格,dis2[i]为终点到i点的最高价格(均包括i点),则dis2[i] - dis1[i]就是以i点为两次交易中间点时的收益

一个点能进行交易的条件是1能到这个点,然后这个点又能走到终点,若我们正向BFS可到达这个点,说明这个点可买入,若反向BFS可到达这个点,则说明这个点可卖出

其实就是正向加反向BFS扫两遍,注意判重,要放在更新答案之后,也不用担心后面的答案又会变小什么的,因为即使 u-v,v-u 出现回路,使得答案会改变,若v点买入价比u点低,并且可以到终点(进行交易的充要条件),则我们反向BFS的时候一定可以更新到v点,只要最后确认答案的时候O(n)地扫一边所有的点,就可以找到最优的答案

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
#define debug(x) cerr << #x << "=" << x << endl;
queue <int> q1, q2;
const int MAXM = 500010 * 2;
const int MAXN = 100000 + 10;
const int INF = 1<<30;
inline void rd(int &x) {
    x = 0;
    char ch = getchar();
    while(ch < '0' || ch >'9') ch = getchar();
    while(ch >='0' && ch <='9') x = x*10 + ch - '0', ch = getchar();
    return;
}
int n,m,last[MAXN],vis[MAXN],ans,sale,buy,tot,value[MAXN],num,revvis[MAXN];
int revlast[MAXN],dis1[MAXN], dis2[MAXN];

struct Edge{
    int u,v,to;
    Edge(){}
    Edge(int u, int v, int to) : u(u), v(v), to(to) {}
}e[MAXM],rev[MAXM];

inline void add(int u, int v) {
    e[++tot] = Edge(u,v,last[u]);
    last[u] = tot;
}

inline void addrev(int u, int v) {
    rev[++num] = Edge(u,v,revlast[u]);
    revlast[u] = num;
}

void bfs1() {
    memset(dis1,0x3f,sizeof(dis1));
    dis1[1] = value[1];
    q1.push(1);
    vis[1] = 1;
    while(!q1.empty()) {
        int x = q1.front();
        q1.pop();
        for(int i=last[x]; i; i=e[i].to) {
            int v = e[i].v;
            dis1[v] = min(dis1[x], value[v]);
            if(!vis[v]) {
                vis[v] = 1;
                q1.push(v);
            }
        }
    }
}

void bfs2() {
    dis2[n] = value[n];
    q2.push(n);
    revvis[n] = 1;
    while(!q2.empty()) {
        int x = q2.front();
        q2.pop();
        for(int i=revlast[x]; i; i=rev[i].to) {
            int v = rev[i].v;
            dis2[v] = max(dis2[x], value[v]);
            if(!revvis[v]) {
                revvis[v] = 1;
                q2.push(v);
            }
        }
    }
}

int main() {
    rd(n), rd(m);
    for(int i=1; i<=n; i++)
        rd(value[i]);
    for(int i=1; i<=m; i++) {
        int x,y,z;
        rd(x), rd(y), rd(z);
        add(x,y);
        addrev(y,x);
        if(z == 2)
            add(y,x), addrev(x,y);
    }
    bfs1();
    bfs2();
    for(int i=1; i<=n; i++)
        ans = max(ans, dis2[i] - dis1[i]);
    printf("%d\n", ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值