POJ 3635 Full Tank(最短路径变形 + 优先队列)

题意:

给 n 个城市 m 条路,每个城市有个加油站,但是收费单价不同,现给你几个查询并给定汽车的装油量, 问你从 S 城市到 T 城市的最小费用。

思路:

1. 如果是一条路径上面的加油站问题,比较容易解决,有动规方程:dp[i][j] 即到 i 站还剩下 j 升油的费用;

2. 现在涉及到图中的类似问题,并且数据规模还挺大的,直接针对每条边都进行一次转移,会 TLE,因为中间产生了很多无用的状态;

3. 因为要求最小费用,如果到达 T 时恰好油量为 0,则此时肯定是最优解的条件之一了。所以这个时候我们借鉴 dijstra 中最短路径的思路,

   每次只加 1 升油,等价于把每个加油站又分解为 C + 1 个点,利用贪心的策略,每次寻找最小的费用即可.

 

#include <iostream>

#include <algorithm>

#include <queue>

using namespace std;



const int MAXN = 1010;

const int MAXM = 110;

const int INFS = 0x3fffffff;



struct ST {

    int u, fuel, cost;

    ST(int _u, int _fuel, int _cost) : u(_u), fuel(_fuel), cost(_cost) {}

    bool operator < (const ST& other) const { return cost > other.cost; }

};



struct edge {

    int v, d;

    edge* next;

} *V[MAXN], ES[MAXN*10*2];



int EC, price[MAXN], dp[MAXN][MAXM];

bool vis[MAXN][MAXM];



void addedge(int u, int v, int d) {

    ES[++EC].next = V[u];

    V[u] = ES + EC;

    V[u]->v = v; V[u]->d = d;

}



int bfs(int s, int t, int vol) {

    priority_queue<ST> Q;

    Q.push(ST(s, 0, 0));

    dp[s][0] = 0;



    while (!Q.empty()) {

        int u = Q.top().u;

        int fuel = Q.top().fuel;

        int cost = Q.top().cost;

        Q.pop();



        vis[u][fuel] = true;



        if (u == t)

            return cost;



        if (fuel + 1 <= vol && !vis[u][fuel+1] &&

            dp[u][fuel+1] > dp[u][fuel] + price[u]) {

                dp[u][fuel+1] = dp[u][fuel] + price[u];

                Q.push(ST(u, fuel+1, dp[u][fuel+1]));

        }



        for (edge* e = V[u]; e; e = e->next) {

            int v = e->v;

            int f = fuel - e->d;

            if (f >= 0 && !vis[v][f] && dp[v][f] > cost) {

                dp[v][f] = cost;

                Q.push(ST(v, f, cost));

            }

        }

    }

    return -1;

}



int main() {

    int n, m;

    while (~scanf("%d%d", &n, &m)) {

        for (int i = 0; i < n; i++)

            scanf("%d", &price[i]);



        EC = 0;

        memset(V, 0, sizeof(V));

        for (int i = 0; i < m; i++) {

            int u, v, d;

            scanf("%d%d%d", &u, &v, &d);

            addedge(u, v, d);

            addedge(v, u, d);

        }

        int cases;

        scanf("%d", &cases);

        while (cases--) {

            int vol, s, t;

            scanf("%d%d%d", &vol, &s, &t);



            for (int i = 0; i <= n; i++)

                for (int j = 0; j <= vol; j++)

                    dp[i][j] = INFS, vis[i][j] = false;



            int ans = bfs(s, t, vol);

            if (ans != -1)

                printf("%d\n", ans);

            else

                printf("impossible\n");

        }

    }



    return 0;

}

你可能感兴趣的:(最短路径)