题目地址:
https://leetcode.com/problems/minimum-cost-to-reach-city-with-discounts/description/
给定一个 n n n个顶点 m m m条边的带权无向图,顶点编号 0 ∼ n − 1 0\sim n-1 0∼n−1。要从 0 0 0走到 n − 1 n-1 n−1,再给定 d d d次机会,每次走一条边的时候可以使用 1 1 1次机会使得边权以原来的一半走过。问最短路长度。
分层图最短路问题,将 ( u , g ) (u, g) (u,g)看成一个整体, u u u为顶点, g g g为还剩多少次“半价”的机会。那么问题转为在新图上的最短路问题,可以用Dijkstra算法来做。代码如下:
class Solution {
public:
#define x first
#define y second
using PII = pair<int, int>;
vector<int> h, e, ne, w;
int idx;
#define add(a, b, c) e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++
int minimumCost(int n, vector<vector<int>> &es, int d) {
int m = es.size() * 2;
h.resize(n, -1);
e.resize(m);
ne.resize(m);
w.resize(m);
idx = 0;
for (auto &ed : es) add(ed[0], ed[1], ed[2]), add(ed[1], ed[0], ed[2]);
int dist[n][d + 1];
memset(dist, 0x3f, sizeof dist);
dist[0][d] = 0;
auto cmp = [&](const PII &p1, const PII &p2) {
return dist[p1.x][p1.y] > dist[p2.x][p2.y];
};
priority_queue<PII, vector<PII>, decltype(cmp)> heap(cmp);
heap.push({0, d});
bool vis[n][d + 1];
memset(vis, 0, sizeof vis);
while (heap.size()) {
auto t = heap.top();
heap.pop();
int u = t.x, d = t.y;
if (u == n - 1) return dist[u][d];
if (vis[u][d]) continue;
vis[u][d] = true;
for (int i = h[u]; ~i; i = ne[i]) {
int v = e[i];
if (!vis[v][d] && dist[v][d] > dist[u][d] + w[i]) {
dist[v][d] = dist[u][d] + w[i];
heap.push({v, d});
}
if (d && !vis[v][d - 1] && dist[v][d - 1] > dist[u][d] + w[i] / 2) {
dist[v][d - 1] = dist[u][d] + w[i] / 2;
heap.push({v, d - 1});
}
}
}
return -1;
}
};
时间复杂度 O ( n d log ( n d m ) ) O(nd\log (ndm)) O(ndlog(ndm)),空间 O ( n d ) O(nd) O(nd)。