最优贸易

https://loj.ac/problem/2590

题目描述

  阿龙要从1号节点到n号节点,每个节点对于一种商品有买入价和卖出价,同一节点买入价和卖出价相同,求在只执行一次买卖最多可以转多少钱。(可从夫经过节点)。

思路

  首先我们考虑对于同一节点的买卖状态,我们只有3种:买,卖,不买不卖。那么显然,我们只需要对于每一个状态建一层图,在不买不卖和买之间连边,在买和卖之间连边,由于只会进行一次贸易,所以实际两种的状态我们分为三种(实际只有有水晶球和无水晶球),卖完之后无法再回到不买不卖这一层,这样就保证了只进行一次贸易。在这张图上跑一遍最短路,由于存在负权,我们只能跑spfa。

代码

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10,M=1e6+20;
int nxt[M],to[M],w[M],head[N],tot,n,m;
int v[N],dis[N<<2];
bool exist[N<<2];
void add_edge(int x,int y,int v)
{
    nxt[++tot]=head[x];
    head[x]=tot;
    to[tot]=y;
    w[tot]=v;
}
void build()
{
    for(int i=1;i<=n;i++)
    {
        add_edge(i,i+n,v[i]);
        add_edge(i+n,i+n*2,-v[i]);
    }
}
void spfa()
{
    memset(dis,0x3f,sizeof(dis));
    queue<int>q;
    dis[1]=0;q.push(1);exist[1]=1;
    while(!q.empty())
    {
        int u=q.front();q.pop();
        exist[u]=0;
        for(int i=head[u];i;i=nxt[i])
        {
            int v=to[i];
            if(dis[v]>dis[u]+w[i])
            {
                dis[v]=dis[u]+w[i];
                if(!exist[v])
                {
                    q.push(v);
                    exist[v]=1;
                }
            }
        }
    }
}
int main() 
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&v[i]);
    for(int i=1;i<=m;i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        if(z==1)add_edge(x,y,0),add_edge(x+n,y+n,0),add_edge(x+n*2,y+n*2,0);
        else
        {
            add_edge(x,y,0);add_edge(x+n,y+n,0);add_edge(x+n*2,y+n*2,0);
            add_edge(y,x,0);add_edge(y+n,x+n,0);add_edge(y+n*2,x+n*2,0)    ;
        }    
    }
    build();
    spfa();
    int ans=0x3f3f3f3f;
    for(int i=0;i<3;i++)
        ans=min(ans,dis[i*n+n]);
    printf("%d",-ans);
    return 0;
}
原文地址:https://www.cnblogs.com/fangbozhen/p/11683232.html