ZOJ-3794 Greedy Driver(最短路径)

题意

一张 n 个节点, m 条边的有向图,你的车在 1 号节点,要开到 n 节点,容量为 C (初始时满油)。有若干个点可以无限免费加油,又有若干个地方可以以当地的油价卖出任意容积的油。求从 1 n 通过一次卖油赚取的最大价值。
1 n 1000
1 m 100000

思路

由于卖油的地方只有一个,而且只能卖一次,所以这个点肯定是枚举的。构建一张正向图,求出从 1 出发,开到 i ,能保有的最大油量,再构建一张反向图,求出从 n 出发,开到 i ,能花费的最少油量(就是正向图上从 i n 的最小花费)。那这两个数作差就是在 i 这个点多余,可以卖掉的油。
这道题的最短路和最长路没有 d i j k s t r a 的单调性,能保有的油量在开到加油站时返增不减。这种最短路和最长路问题,用 s p f a 往往可以方便的解决,但是 s p f a 极限复杂度是 O ( n m ) 的,在 n m 的值很大时,往往有其他的解法, s p f a 应能避就避。

代码

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#define FOR(i,x,y) for(int i=(x);i<=(y);i++)
#define DOR(i,x,y) for(int i=(x);i>=(y);i--)
#define N 1003
#define M 100003
typedef long long LL;
using namespace std;
int dis1[N],dis2[N],sell[N];
template<const int maxn,const int maxm>struct Linked_list
{
    int head[maxn],to[maxm],cost[maxm],nxt[maxm],tot;
    void clear(){memset(head,-1,sizeof(head));tot=0;}
    void add(int u,int v,int w){to[++tot]=v,cost[tot]=w,nxt[tot]=head[u];head[u]=tot;}
    #define EOR(i,G,u) for(int i=G.head[u];~i;i=G.nxt[i])
};
Linked_list<N,M>G;
Linked_list<N,M>R;
bool addable[N],vis[N];
int n,m,C;
void solve1(int s)
{
    queue<int>q;
    while(!q.empty())q.pop();
    FOR(i,1,n)dis1[i]=-1;
    memset(vis,0,sizeof(vis));
    dis1[s]=C;vis[s]=1;
    q.push(s);
    while(!q.empty())
    {
        int u=q.front();q.pop();vis[u]=0;
        if(addable[u])dis1[u]=C;
        EOR(i,G,u)
        {
            int v=G.to[i],w=G.cost[i];
            if(dis1[u]-w>dis1[v]&&dis1[u]-w>=0)
            {
                dis1[v]=dis1[u]-w;
                if(!vis[v])
                {
                    vis[v]=1;
                    q.push(v);
                }
            }
        }
    }
}
void solve2(int s)
{
    queue<int>q;
    while(!q.empty())q.pop();
    FOR(i,1,n)dis2[i]=C+1;
    memset(vis,0,sizeof(vis));
    dis2[s]=0;vis[s]=1;
    q.push(s);
    while(!q.empty())
    {
        int u=q.front();q.pop();vis[u]=0;
        if(addable[u])dis2[u]=0;
        EOR(i,R,u)
        {
            int v=R.to[i],w=R.cost[i];
            if(dis2[u]+w<dis2[v])
            {
                dis2[v]=dis2[u]+w;
                if(!vis[v])
                {
                    vis[v]=1;
                    q.push(v);
                }
            }
        }
    }
}

int main()
{
    while(~scanf("%d%d%d",&n,&m,&C))
    {
        LL ans=-1;
        G.clear();R.clear();
        memset(sell,0,sizeof(sell));
        FOR(i,1,m)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            G.add(u,v,w);
            R.add(v,u,w);
        }
        memset(addable,0,sizeof(addable));
        int t,u,x;
        scanf("%d",&t);
        while(t--)
        {
            scanf("%d",&u);
            addable[u]=1;
        }
        solve1(1);
        solve2(n);
        scanf("%d",&t);
        while(t--)
        {
            scanf("%d%d",&u,&x);
            sell[u]=x;
        }
        FOR(i,1,n)
            if(dis1[i]>=dis2[i])
                ans=max(ans,1LL*(dis1[i]-dis2[i])*sell[i]);
        printf("%lld\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Paulliant/article/details/81674477