ZOJ_3794_Greedy Driver(单源最短路)

题型:图论


题意:

      n个city,m条边。一个司机从1点开车到n点,每条边都有耗油量w,有一些点是加油站,有一些点是可以卖油点且每个卖油点都有各自的卖油单价val。司机遇到加油站是就可以把油加满,中途只能卖一次油。问司机从1点出发至n点后最多可以赚多少油钱。


分析:

      直观上,每条路径上安排几个卖油点或者是几个加油站是做不到的。转而想,既然中途只能买一次油,那么只需枚举卖油点即可。

      当到达卖油点 i 时,设从起点到该卖油点所剩的最大油量为Left,从该卖油点抵达终点所需的最少耗油量为Cost,则有

                                              Sell = Left - Cost

      要追求的是最大的Sell量,所以需要Left尽量大而cost尽量小。

      对于Left的求解,可以想象成为从起点到各个点的最短路径的变形,求到每个点的油最大剩余量,起点油量为c,遇到加油站时,剩余油量变为c。

      对于Cost的求解,可以想象成为从终点到各个点的最短路径的变形,求终点到各个点的最少耗油量,终点耗油量为0,遇到加油站耗油量为0。 


代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<queue>
#define mt(a,b) sizeof(a,b,sizeof(a));
#define M 1234
#define INF 0x3f3f3f3f
using namespace std;
int n,m,c;

struct G {
    struct E {
        int u;
        int v;
        int w;
        int next;
    } e[M*M];
    int le,head[M];
    void init() {
        le = 0;
        memset(head,-1,sizeof(head));
    }
    void add(int u,int v,int w) {
        e[le].u = u;
        e[le].v = v;
        e[le].w = w;
        e[le].next = head[u];
        head[u] = le++;
    }
} g1,g2; //原图和反图

int dis1[M],dis2[M];
bool used[M];

bool station[M],sell[M];

void SPFA_from_start() {

    queue<int> q;
    q.push(1);
    used[1] = true;
    dis1[1] = c;
    while(!q.empty()) {
        int u = q.front();
        q.pop();
        used[u] = false;
        for(int i=g1.head[u]; i!=-1; i=g1.e[i].next) {
            int v = g1.e[i].v;
            int w = g1.e[i].w;
            if(dis1[u] < w) continue;
            if(station[v]) {
                if(dis1[v]<c)
                {
                    dis1[v] = c;
                    if(!used[v]) {
                        used[v] = true;
                        q.push(v);
                    }
                }
            } else {
                if(dis1[v] < dis1[u] - w) {
                    dis1[v] = dis1[u] - w;
                    if(!used[v]) {
                        used[v] = true;
                        q.push(v);
                    }
                }
            }
        }
    }
}


void SPFA_from_end() {

    queue<int> q;
    q.push(n);
    used[n] = true;
    dis2[n] = 0;
    while(!q.empty()) {
        int u = q.front();
        q.pop();
        used[u] = false;
        for(int i=g2.head[u]; i!=-1; i=g2.e[i].next) {
            int v = g2.e[i].v;
            int w = g2.e[i].w;
            if(dis2[u]+w>c)continue;
            if(station[v]) {
                if(dis2[v]>0) {
                    dis2[v]=0;
                    if(!used[v]) {
                        used[v]=true;
                        q.push(v);
                    }
                }
            } else {
                if(dis2[v]>dis2[u]+w) {
                    dis2[v]=dis2[u]+w;
                    if(!used[v]) {
                        used[v]=true;
                        q.push(v);
                    }
                }
            }
        }
    }
}

int main() {
    int P,Q;
    while(~scanf("%d%d%d",&n,&m,&c)) {
        g1.init();
        g2.init();
        int u,v,w;
        for(int i=0; i<m; i++) {
            scanf("%d%d%d",&u,&v,&w);
            g1.add(u,v,w);
            g2.add(v,u,w);
        }

        memset(station,false,sizeof(station));
        memset(sell,false,sizeof(sell));
        scanf("%d",&P);
        int tmp;
        for(int i=0; i<P; i++) {
            scanf("%d",&tmp);
            station[tmp] = true;
        }

        memset(dis1,-1,sizeof(dis1));
        for(int i=0; i<=M; i++) {
            dis2[i] = INF;
        }

        memset(used,false,sizeof(used));
        SPFA_from_start();
        memset(used,false,sizeof(used));
        SPFA_from_end();
        scanf("%d",&Q);
        int id,value;
        int ans = 0;
        while(Q--){
            scanf("%d%d",&id,&value);
            if(dis1[id]>=0&&dis2[id]<=c) {
                ans=max(ans,(dis1[id]-dis2[id])*value);
            }
        }
        if(dis1[n]<0)ans=-1;
        printf("%d\n",ans);

    }

    return 0;
}
/*
5 6 10
1 2 4
1 4 1
4 3 1
2 5 1
4 5 2
3 2 1
1
3
1
2 2
*/


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值