Daily DSA: Minimum Cost with Discount Vouchers (Medium)
Problem Description
You are given an integer n representing the number of cities numbered from 0 to n - 1, and a 2D integer array flights where flights[i] = [u_i, v_i, cost_i] indicates a directed flight from city u_i to city v_i with a specific cost_i.
You are also given two integers src and dst, representing the starting and destination cities, and an integer k, representing the maximum number of discount vouchers you have. Each voucher allows you to take any single flight at half price, rounded down (i.e., cost // 2). You can use at most one voucher per flight leg, and you can use at most k vouchers during the entire journey.
Return the minimum total cost to travel from src to dst. If it is impossible to reach dst from src, return -1.
Examples
Example 1:
Input: n = 4, flights = [[0,1,100],[1,2,100],[2,3,100],[0,2,500]], src = 0, dst = 3, k = 1
Output: 250
Explanation:
The optimal route is 0 -> 1 -> 2 -> 3 with costs 100, 100, 100.
Apply the voucher on leg 0 -> 1 (cost becomes 50), total cost = 50 + 100 + 100 = 250.
Alternatively, applying it to any of the 100-cost legs gives 250.
Example 2:
Input: n = 3, flights = [[0,1,10],[1,2,20],[0,2,100]], src = 0, dst = 2, k = 2
Output: 15
Explanation:
Take route 0 -> 1 -> 2. Apply vouchers to both legs:
- 0 -> 1: cost 10 // 2 = 5
- 1 -> 2: cost 20 // 2 = 10
Total cost = 5 + 10 = 15.
Example 3:
Input: n = 3, flights = [[0,1,10]], src = 0, dst = 2, k = 1
Output: -1
Explanation:
There is no path from city 0 to city 2.
Constraints
1 <= n <= 10^40 <= flights.length <= 5 * 10^4flights[i].length == 30 <= u_i, v_i < nu_i != v_i1 <= cost_i <= 10^50 <= src, dst < n0 <= k <= 10
Approach
This problem can be modeled as finding the shortest path on a layered graph (state-space graph) using Dijkstra’s Algorithm.
- State Representation:
- Each state can be defined as
(cost, u, used_vouchers), where:cost: Total cost accumulated so far.u: Current city.used_vouchers: Number of discount vouchers used so far (0 <= used_vouchers <= k).
- Each state can be defined as
- Transitions from state
(u, used):- For every outgoing flight
(u -> v, price):- Without using a voucher: Move to
(v, used)with costcurrent_cost + price. - Using a voucher (if
used < k): Move to(v, used + 1)with costcurrent_cost + (price / 2).
- Without using a voucher: Move to
- For every outgoing flight
- Data Structures:
- A 2D distance array
dist[u][used]initialized to infinity, storing the minimal cost to reach cityuwithusedvouchers. - A min-heap (priority queue) storing tuples of
(cost, u, used)ordered bycost.
- A 2D distance array
- Termination:
- The first time we pop destination city
dstfrom the priority queue, the associated cost is guaranteed to be minimal. - If the queue becomes empty and
dstwas never reached, return-1.
- The first time we pop destination city
C++ Solution
#include <vector>
#include <queue>
#include <tuple>
using namespace std;
class Solution {
public:
int minimumCostWithVouchers(int n, vector<vector<int>>& flights, int src, int dst, int k) {
// Build adjacency list: u -> vector of {v, cost}
vector<vector<pair<int, int>>> adj(n);
for (const auto& flight : flights) {
adj[flight[0]].emplace_back(flight[1], flight[2]);
}
// dist[city][vouchers_used]
const long long INF = 1e18;
vector<vector<long long>> dist(n, vector<long long>(k + 1, INF));
// Min-heap storing {cost, u, vouchers_used}
priority_queue<tuple<long long, int, int>,
vector<tuple<long long, int, int>>,
greater<tuple<long long, int, int>>> pq;
dist[src][0] = 0;
pq.emplace(0, src, 0);
while (!pq.empty()) {
auto [d, u, used] = pq.top();
pq.pop();
if (u == dst) return d;
if (d > dist[u][used]) continue;
for (const auto& [v, cost] : adj[u]) {
// Option 1: Do not use a voucher
if (d + cost < dist[v][used]) {
dist[v][used] = d + cost;
pq.emplace(dist[v][used], v, used);
}
// Option 2: Use a voucher (if available)
if (used < k) {
int discounted_cost = cost / 2;
if (d + discounted_cost < dist[v][used + 1]) {
dist[v][used + 1] = d + discounted_cost;
pq.emplace(dist[v][used + 1], v, used + 1);
}
}
}
}
return -1;
}
};
Complexity Analysis
- Time Complexity: $\mathcal{O}((N \cdot K + M \cdot K) \log(N \cdot K))$, where $N$ is the number of cities, $M$ is the number of flights, and $K$ is the maximum number of vouchers.
- Space Complexity: $\mathcal{O}(N \cdot K + M)$ to store the distance table, graph adjacency list, and priority queue.