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;
}
原文地址:https://www.cnblogs.com/kedebug/p/2995244.html