题解 | #[NOIP2009]最优贸易#

[NOIP2009]最优贸易

https://ac.nowcoder.com/acm/problem/16611

题意

在 从 1号点到 n号点 的路径中, 选择两个城市 A 和 B,必须先选 A 再选 B,问 能赚取的差价最大是多少
A:买入水晶球
B:卖出水晶球

ps:1 号点到 n 号点的路径可能不止一条 .

思路

不妨遍历 n 个点,对于每个点求出 res_i = w2[i] - w1[i]
w1[i]:买入。在从 1 到 i 的路径中,水晶球的最小价格 (若从 1 到不了 i ,则 w1[i] = 正无穷 )
w2[i]:卖出。在从 n 到 i 的路径中,水晶球的最大价格 (若从 n 到不了 i ,则 w2[i] = 0 ) // 要求我们建立反向边

因此最后的 ans = max( 0, res_1, res_2, ...... , res_n )

求 w1[ ] :正向 spfa
求 w2[ ] :反向 spfa ,要求我们建立反向边,比如 a ==> b 的边,我们就建立 b ==> a 的边

ps:此处不可以使用 dijkstra ,因为 dijkstra 是从起点开始依次选择距离起点最短的边,从而形成的最短路,而此题 一方面 不是让求最短路,一方面 没有边权.

综上,Code 如下.

Code

#include <bits/stdc++.h>

using namespace std;

const int N=100010,M=500010;

int a[N];
bool st1[N],st2[N];
int h1[N],e1[M],ne1[M],idx1;
int h2[N],e2[M],ne2[M],idx2;
int w1[N],w2[N];
int n,m;

void add1(int a,int b){   // 建立正向边
    e1[idx1]=b,ne1[idx1]=h1[a],h1[a]=idx1++;
}

void add2(int a,int b){  // 建立反向边
    e2[idx2]=b,ne2[idx2]=h2[a],h2[a]=idx2++;
}

void spfa1(){  // 从起点开始
    memset(w1,0x3f,sizeof w1);
    st1[1]=true;
    queue<int>q;
    q.push(1);
    w1[1]=a[1];

    while(q.size()){
        int t=q.front();
        q.pop();
        st1[t]=false;

        for(int i=h1[t];~i;i=ne1[i]){
            int j=e1[i];
            if(a[j]<w1[j]||w1[t]<w1[j]){ // 存在回路,即可能 w1[j] 比两者都小(或等于),此时不需要更新
                w1[j]=min(w1[t],a[j]);     // j 点被更新
                if(!st1[j]) st1[j]=true,q.push(j);  // 被更新的点 其 后面的点可能也需要更新 所以 j 入列
            }
        }
    }
}

void spfa2(){  //从终点开始
    st2[n]=true;
    queue<int>q;
    q.push(n);
    w2[n]=a[n];

    while(q.size()){
        int t=q.front();
        q.pop();
        st2[t]=false;
        for(int i=h2[t];~i;i=ne2[i]){
            int j=e2[i];
            if(a[j]>w2[j]||w2[t]>w2[j]){  // 原因同上
                w2[j]=max(a[j],w2[t]);  
                if(!st2[j]) st2[j]=true,q.push(j);
            }
        }
    }
}

int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>a[i];
    memset(h1,-1,sizeof h1);
    memset(h2,-1,sizeof h2);

    while(m--){
        int x,y,z;
        cin>>x>>y>>z;
        if(z==1) add1(x,y),add2(y,x);  //同时建立反向边
        else add1(x,y),add1(y,x),add2(x,y),add2(y,x);   //同时建立反向边
    }

    spfa1();
    spfa2();

    int res=0;
    for(int i=1;i<=n;i++) res=max(res,w2[i]-w1[i]); 
    cout<<res<<endl;

    return 0;
}
全部评论

相关推荐

水墨不写bug:疑似没有上过大学
点赞 评论 收藏
分享
05-11 11:48
河南大学 Java
程序员牛肉:我是26届的双非。目前有两段实习经历,大三上去的美团,现在来字节了,做的是国际电商的营销业务。希望我的经历对你有用。 1.好好做你的CSDN,最好是直接转微信公众号。因为这本质上是一个很好的展示自己技术热情的证据。我当时也是烂大街项目(网盘+鱼皮的一个项目)+零实习去面试美团,但是当时我的CSDN阅读量超百万,微信公众号阅读量40万。面试的时候面试官就告诉我说觉得我对技术挺有激情的。可以看看我主页的美团面试面经。 因此花点时间好好做这个知识分享,最好是单拉出来搞一个板块。各大公司都极其看中知识落地的能力。 可以看看我的简历对于博客的描述。这个帖子里面有:https://www.nowcoder.com/discuss/745348200596324352?sourceSSR=users 2.实习经历有一些东西删除了,目前看来你的产出其实很少。有些内容其实很扯淡,最好不要保留。有一些点你可能觉得很牛逼,但是面试官眼里是减分的。 你还能负责数据库表的设计?这个公司得垃圾成啥样子,才能让一个实习生介入数据库表的设计,不要写这种东西。 一个公司的财务审批系统应该是很稳定的吧?为什么你去了才有RBAC权限设计?那这个公司之前是怎么处理权限分离的?这些东西看着都有点扯淡了。 还有就是使用Redis实现轻量级的消息队列?那为什么这一块不使用专业的MQ呢?为什么要使用redis,这些一定要清楚, 就目前看来,其实你的这个实习技术还不错。不要太焦虑。就是有一些内容有点虚了。可以考虑从PR中再投一点产出
投递美团等公司8个岗位
点赞 评论 收藏
分享
评论
2
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务