Vijos1754 最优贸易(SPFA)

题意分析

反向建图的思想应该是很常见的。
一开始一直在想直接做一次SPFA求出结果,发现一直无法维护找到的最大最小值和能够更新的先后关系。后来看了一下题解才明白怎么回事。

首先正向建图,因为一开始是没有购买水晶球的,首先应该找一个地方购买水晶球,按照贪心的思想,希望购买到的水晶球的价钱尽量低。这时候做SPFA,意义为找到从起点开始能到达的点,其能买到水晶球最便宜的价格为多少。
SPFA的过程也不难,松弛的条件为,如果下一个节点的最小值比当前点最小值小,即做松弛。 注意松弛的时候应该将下一个节点的值更新为 min(当前节点的值,下一个节点的值),才能保证下一个节点的值为最小。

然后反向见图。 反向见图的含义是,如果我买到了水晶球,从哪卖出去能够获得最大收益。还是按照贪心的思想,希望卖出的水晶球的价格越高越好,SPFA过程维护的是,从终点开始能够到达的点,其能卖出水晶球的最贵的价格为多少。
SPFA的过程松弛条件与刚才相反,遇到更大的就松弛。

结果为所有点对应的卖出与买入的差值最大值,遍历求出即可。

代码总览

#include<bits/stdc++.h>
using namespace std;
const int nmax = 500005;
const int mmax = 100005;
typedef struct{
    int to,nxt;
} Edge;
Edge e[nmax<<1], e2[nmax<<1];
int a[mmax],head[nmax<<1],head2[nmax<<1];
int p[mmax],p2[mmax];
int n,m,tot = 0,tot2 = 0;
void add(int u,int v){
    e[tot].to = v;
    e[tot].nxt = head[u];
    head[u] = tot++;
}
void add2(int u,int v){
    e2[tot2].to = v;
    e2[tot2].nxt = head2[u];
    head2[u] = tot2++;
}
void spfa(){
    memset(p,0x3f3f3f3f,sizeof p); p[1] = a[1];
    bitset<mmax> inque; inque.reset();
    queue<int> q; q.push(1);
    while(!q.empty()){
        int u = q.front(); q.pop(); inque.reset(u);
        for(int i = head[u]; i!=-1; i=e[i].nxt){
            int v = e[i].to;
            if(p[v] > p[u]){
                p[v] = min(p[u],a[v]);
                if(!inque.test(v)){
                    q.push(v);
                    inque.set(v);
                }
            }
        }
    }
}
void spfa2(){
    memset(p2,-1,sizeof p2); p2[n] = a[n];
    bitset<mmax> inque; inque.reset();
    queue<int> q; q.push(n);
    while(!q.empty()){
        int u = q.front(); q.pop(); inque.reset(u);
        for(int i = head2[u]; i!=-1; i=e2[i].nxt){
            int v = e2[i].to;
            if(p2[v] < p2[u]){
                p2[v] = max(p2[u],a[v]);
                if(!inque.test(v)) {
                    q.push(v);
                    inque.set(v);
                }
            }
        }
    }
}
int main(){
    memset(head,-1,sizeof head);
    memset(head2,-1,sizeof head2);
    scanf("%d %d",&n,&m);
    for(int i = 1;i<=n;++i) scanf("%d",&a[i]);
    int u,v,w;
    for(int i = 1;i<=m;++i) {
        scanf("%d %d %d",&u,&v,&w);
        if(w == 1){add(u,v) ; add2(v,u);}
        else {add(u,v);add(v,u);add2(u,v);add2(v,u);}
    }
    spfa();
    spfa2();
    int ans = 0;
    for(int i = 1;i<=n;++i)
        if(p[i]!=0x3f3f3f3f && p2[i]!=-1 && p2[i] - p[i] > ans) ans = p2[i] - p[i];
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值