Minimum Cost to Reach City With Discounts

medium graph dijkstra shortest path

Problem

A country has n cities connected by bidirectional highways[i] = [city1, city2, toll]. You start in city 0 and want to reach city n − 1. You have a number of discounts; each discount lets you take one highway for half its toll (rounded down). Each discount may be used on at most one highway, and each highway may carry at most one discount. Return the minimum total cost to reach city n − 1, or -1 if it cannot be reached.

The twist is that the cheapest route depends on how many discounts you have left, so the search state is the pair (city, discounts used). We run Dijkstra over this expanded graph: from a state, each highway branches into a full-toll move (same discount count) and, if discounts remain, a half-toll move (one more used).

Inputn = 5, highways = [[0,1,4],[2,1,3],[1,4,11],[3,2,3],[3,4,2]], discounts = 1
Output9
Take 0→1 at full toll 4, then 1→4 with the discount (11 → 5), for a total of 9.

import heapq

def minimum_cost(n, highways, discounts):
    adj = {i: [] for i in range(n)}
    for u, v, t in highways:
        adj[u].append((v, t))
        adj[v].append((u, t))
    INF = float('inf')
    dist = [[INF] * (discounts + 1) for _ in range(n)]
    dist[0][0] = 0
    pq = [(0, 0, 0)]
    while pq:
        d, u, k = heapq.heappop(pq)
        if d > dist[u][k]:
            continue
        for v, t in adj[u]:
            if d + t < dist[v][k]:
                dist[v][k] = d + t
                heapq.heappush(pq, (d + t, v, k))
            if k < discounts:
                nd = d + t // 2
                if nd < dist[v][k + 1]:
                    dist[v][k + 1] = nd
                    heapq.heappush(pq, (nd, v, k + 1))
    ans = min(dist[n - 1])
    return ans if ans < INF else -1
function minimumCost(n, highways, discounts) {
  const adj = Array.from({ length: n }, () => []);
  for (const [u, v, t] of highways) {
    adj[u].push([v, t]);
    adj[v].push([u, t]);
  }
  const dist = Array.from({ length: n }, () => new Array(discounts + 1).fill(Infinity));
  dist[0][0] = 0;
  const pq = [[0, 0, 0]];
  while (pq.length) {
    pq.sort((a, b) => a[0] - b[0]);
    const [d, u, k] = pq.shift();
    if (d > dist[u][k]) continue;
    for (const [v, t] of adj[u]) {
      if (d + t < dist[v][k]) {
        dist[v][k] = d + t;
        pq.push([d + t, v, k]);
      }
      if (k < discounts) {
        const nd = d + Math.floor(t / 2);
        if (nd < dist[v][k + 1]) {
          dist[v][k + 1] = nd;
          pq.push([nd, v, k + 1]);
        }
      }
    }
  }
  const ans = Math.min(...dist[n - 1]);
  return ans === Infinity ? -1 : ans;
}
class Solution {
    public int minimumCost(int n, int[][] highways, int discounts) {
        List<int[]>[] adj = new List[n];
        for (int i = 0; i < n; i++) adj[i] = new ArrayList<>();
        for (int[] h : highways) {
            adj[h[0]].add(new int[]{h[1], h[2]});
            adj[h[1]].add(new int[]{h[0], h[2]});
        }
        int[][] dist = new int[n][discounts + 1];
        for (int[] row : dist) Arrays.fill(row, Integer.MAX_VALUE);
        dist[0][0] = 0;
        PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> a[0] - b[0]);
        pq.offer(new int[]{0, 0, 0});
        while (!pq.isEmpty()) {
            int[] top = pq.poll();
            int d = top[0], u = top[1], k = top[2];
            if (d > dist[u][k]) continue;
            for (int[] nb : adj[u]) {
                int v = nb[0], t = nb[1];
                if (d + t < dist[v][k]) {
                    dist[v][k] = d + t;
                    pq.offer(new int[]{d + t, v, k});
                }
                if (k < discounts && d + t / 2 < dist[v][k + 1]) {
                    dist[v][k + 1] = d + t / 2;
                    pq.offer(new int[]{d + t / 2, v, k + 1});
                }
            }
        }
        int ans = Integer.MAX_VALUE;
        for (int k = 0; k <= discounts; k++) ans = Math.min(ans, dist[n - 1][k]);
        return ans == Integer.MAX_VALUE ? -1 : ans;
    }
}
class Solution {
public:
    int minimumCost(int n, vector<vector<int>>& highways, int discounts) {
        vector<vector<pair<int,int>>> adj(n);
        for (auto& h : highways) {
            adj[h[0]].push_back({h[1], h[2]});
            adj[h[1]].push_back({h[0], h[2]});
        }
        vector<vector<int>> dist(n, vector<int>(discounts + 1, INT_MAX));
        dist[0][0] = 0;
        priority_queue<array<int,3>, vector<array<int,3>>, greater<>> pq;
        pq.push({0, 0, 0});
        while (!pq.empty()) {
            auto [d, u, k] = pq.top(); pq.pop();
            if (d > dist[u][k]) continue;
            for (auto& [v, t] : adj[u]) {
                if (d + t < dist[v][k]) {
                    dist[v][k] = d + t;
                    pq.push({d + t, v, k});
                }
                if (k < discounts && d + t / 2 < dist[v][k + 1]) {
                    dist[v][k + 1] = d + t / 2;
                    pq.push({d + t / 2, v, k + 1});
                }
            }
        }
        int ans = INT_MAX;
        for (int k = 0; k <= discounts; k++) ans = min(ans, dist[n - 1][k]);
        return ans == INT_MAX ? -1 : ans;
    }
};
Time: O(E · D · log(V · D)) Space: O(V · D + E)