← Static tasks

787. Cheapest Flights Within K Stops

leetcode

#array#csharp#graph#heap#leetcode#queue#search

Task

: medium

Есть n городов, соединенных некоторым количеством рейсов. Вам дан массив flights, где flights[i] = [fromi, toi, pricei] указывает на то, что существует рейс из города fromi в город toi с ценой pricei.

Также даны три целых числа src, dst и k. Верните самую дешевую цену от src до dst с не более чем k остановками. Если такого маршрута нет, верните -1.

Пример:

Input: n = 4, flights = [[0,1,100],[1,2,100],[2,0,100],[1,3,600],[2,3,200]], src = 0, dst = 3, k = 1

Output: 700

Explanation:

The graph is shown above.

The optimal path with at most 1 stop from city 0 to 3 is marked in red and has cost 100 + 600 = 700.

Note that the path through cities [0,1,2,3] is cheaper but is invalid because it uses 2 stops.

C# solution

matched/original
using System;
using System.Collections.Generic;
public class Solution {
    public int FindCheapestPrice(int n, int[][] flights, int src, int dst, int k) {
        var adj = new List<(int, int)>[n];
        for (int i = 0; i < n; i++) adj[i] = new List<(int, int)>();
        foreach (var flight in flights) {
            adj[flight[0]].Add((flight[1], flight[2]));
        }
        var dist = new int[n];
        Array.Fill(dist, int.MaxValue);
        var q = new Queue<(int node, int distance)>();
        q.Enqueue((src, 0));
        int stops = 0;
        while (stops <= k && q.Count > 0) {
            int sz = q.Count;
            for (int i = 0; i < sz; i++) {
                var (node, distance) = q.Dequeue();
                foreach (var (neighbour, price) in adj[node]) {
                    if (price + distance >= dist[neighbour]) continue;
                    dist[neighbour] = price + distance;
                    q.Enqueue((neighbour, dist[neighbour]));
                }
            }
            stops++;
        }
        return dist[dst] == int.MaxValue ? -1 : dist[dst];
    }
}

C++ solution

auto-draft, review before submit
#include <bits/stdc++.h>
using namespace std;

// Auto-generated C++ draft from the C# solution. Review containers, LINQ and helper types before submit.
class Solution {
public:
    public int FindCheapestPrice(int n, int[][] flights, int src, int dst, int k) {
        var adj = new List<(int, int)>[n];
        for (int i = 0; i < n; i++) adj[i] = new List<(int, int)>();
        foreach (var flight in flights) {
            adj[flight[0]].push_back((flight[1], flight[2]));
        }
        var dist = new int[n];
        Array.Fill(dist, int.MaxValue);
        var q = new queue<(int node, int distance)>();
        q.Enqueue((src, 0));
        int stops = 0;
        while (stops <= k && q.size() > 0) {
            int sz = q.size();
            for (int i = 0; i < sz; i++) {
                var (node, distance) = q.Dequeue();
                foreach (var (neighbour, price) in adj[node]) {
                    if (price + distance >= dist[neighbour]) continue;
                    dist[neighbour] = price + distance;
                    q.Enqueue((neighbour, dist[neighbour]));
                }
            }
            stops++;
        }
        return dist[dst] == int.MaxValue ? -1 : dist[dst];
    }
}

Java solution

matched/original
import java.util.*;

class Solution {
    public int findCheapestPrice(int n, int[][] flights, int src, int dst, int k) {
        List<List<int[]>> adj = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            adj.add(new ArrayList<>());
        }
        for (int[] flight : flights) {
            adj.get(flight[0]).add(new int[]{flight[1], flight[2]});
        }
        int[] dist = new int[n];
        Arrays.fill(dist, Integer.MAX_VALUE);
        Queue<int[]> q = new LinkedList<>();
        q.offer(new int[]{src, 0});
        int stops = 0;

        while (stops <= k && !q.isEmpty()) {
            int sz = q.size();
            for (int i = 0; i < sz; i++) {
                int[] current = q.poll();
                int node = current[0];
                int distance = current[1];
                for (int[] neighbor : adj.get(node)) {
                    int nextNode = neighbor[0];
                    int price = neighbor[1];
                    if (price + distance >= dist[nextNode]) continue;
                    dist[nextNode] = price + distance;
                    q.offer(new int[]{nextNode, dist[nextNode]});
                }
            }
            stops++;
        }
        return dist[dst] == Integer.MAX_VALUE ? -1 : dist[dst];
    }
}

JavaScript solution

matched/original
class Solution {
    findCheapestPrice(n, flights, src, dst, k) {
        const adj = Array.from({ length: n }, () => []);
        for (const [from, to, price] of flights) {
            adj[from].push([to, price]);
        }
        const dist = Array(n).fill(Infinity);
        const q = [[src, 0]];
        let stops = 0;

        while (stops <= k && q.length) {
            const size = q.length;
            for (let i = 0; i < size; i++) {
                const [node, distance] = q.shift();
                for (const [neighbor, price] of adj[node]) {
                    if (price + distance >= dist[neighbor]) continue;
                    dist[neighbor] = price + distance;
                    q.push([neighbor, dist[neighbor]]);
                }
            }
            stops++;
        }
        return dist[dst] === Infinity ? -1 : dist[dst];
    }
}

Explanation

Algorithm

Создайте список смежности, где adj[X] содержит всех соседей узла X и соответствующую цену, которую нужно заплатить, чтобы перейти к соседу. Инициализируйте массив dist, хранящий минимальную цену для достижения узла из узла src. Инициализируйте его большими значениями. Инициализируйте очередь, хранящую пары {node, distance}. Изначально очередь должна содержать только {src, 0}. Создайте переменную stops и установите ее значение равным 0.

Выполняйте поиск в ширину (BFS), пока очередь не станет пустой или пока stops > k. Итерируйте по всем узлам на определенном уровне. Это будет сделано путем запуска вложенного цикла и посещения всех узлов, которые в данный момент находятся в очереди. В каждой паре {node, distance} итерируйте по всем соседям узла. Для каждого соседа проверьте, меньше ли dist[neighbor] чем distance + цена ребра. Если это так, обновите dist[neighbor] и добавьте {neighbor, dist[neighbor]} в очередь.

После итерации по всем узлам на текущем уровне увеличьте stops на один. Мы посетили все узлы на определенном уровне и готовы посетить следующий уровень узлов. Когда мы достигнем условия, при котором либо очередь станет пустой, либо stops == k, у нас будет наш ответ в dist[dst]. Если dist[dst] не изменилось с начального большого значения, значит, мы никогда не достигли его, и следует вернуть -1.

😎