Відстань між вершинами#2965

Відстань між вершинами (Розбір)

Кількість вершин у графі велика, тому для реалізації алгоритму Дейкстри використовуємо чергу з пріоритетом.

Приклад

Граф, наведений у прикладі, має вигляд:

Реалізація алгоритму

Оголосимо константу нескінченність.

#define INF 0x3F3F3F3F

Оголосимо структуру для зберігання зваженого графа. Кожен елемент є списком пар (вершина, відстань).

vector<vector<pair<int, int> > > g;

Функція Dijkstra реалізує алгоритм Дейкстри, починаючи з вершини .

void Dijkstra(vector<vector<pair<int, int>>>& g, vector<int>& d, 
              int start)
{

Оголосимо та ініціалізуємо чергу з пріоритетами . Елементами цієї черги будуть пари (відстань до вершини від витоку, вершина). Таким чином, на вершині черги завжди знаходиться вершина графа з найменшою відстанню від витоку.

priority_queue<pair<int, int>, vector<pair<int, int>>, 
               greater<pair<int, int>>> pq;
pq.push({0, start});

Ініціалізуємо масив найкоротших відстаней . Вершини графа нумеруються з до .

  d = vector<int>(n + 1, INF);
  d[start] = 0;

Продовжуємо алгоритм Дейкстри, поки черга не порожня.

  while (!pq.empty())
  {

Витягуємо пару з вершини черги з пріоритетами.

    pair<int, int> e = pq.top(); pq.pop();
    int v = e.second; // node

Перевіряємо, чи є вершина фіктивною. Якщо відстань до більша, ніж уже обчислене значення , то ігноруємо цю вершину.

    if (e.first > d[v]) continue; // distance > d[v]

Перебираємо всі вершини , суміжні з . Відстань від до дорівнює .

    for (auto edge: g[v])
    {
      int to = edge.first;
      int cost = edge.second;

Якщо ребро (v, to) релаксується, то перераховуємо значення і заносимо пару (d[to], to) в чергу.

      if (d[v] + cost < d[to])
      {
        d[to] = d[v] + cost;
        pq.push({d[to], to});
      }
    }
  }
}

Основна частина програми. Читаємо вхідні дані.

scanf("%d %d %d %d", &n, &m, &start, &fin);
g.resize(n + 1);
for (i = 0; i < m; i++)
{

Читаємо неорієнтоване ребро вагою і додаємо його до графа .

  scanf("%d %d %d", &b, &e, &w);
  g[b].push_back({e, w});
  g[e].push_back({b, w});
}

Запускаємо алгоритм Дейкстри з початкової вершини .

Dijkstra(g, dist, start);

Виводимо шуканий найкоротший шлях dist[fin]. Якщо dist[fin] дорівнює нескінченності, то шуканого шляху не існує.

if (dist[fin] == INF)
  printf("-1\n");
else
  printf("%d\n", dist[fin]);

Реалізація алгоритму — структура edge

Оголосимо константу нескінченність.

#define INF 0x3F3F3F3F

Оголосимо масив найкоротших відстаней .

vector<int> dist;

Структура зберігає інформацію про ребро: вершину , в яку воно йде, та її вагу .

struct edge
{
  int node, dist;
  edge(int node, int dist) : node(node), dist(dist) {}
};

Структура також буде використана в черзі з пріоритетами. Черга зберігає вершини графа, де

На вершині черги буде знаходиться вершина графа з найменшим значенням .

bool operator< (edge a, edge b)
{
  return a.dist > b.dist;
}

Оголосимо список суміжності графа.

vector<vector<edge> > g;

Функція Dijkstra реалізує алгоритм Дейкстри, починаючи з вершини .

void Dijkstra(vector<vector<edge> > &g, vector<int> &d, int start)
{

Оголосимо та ініціалізуємо чергу з пріоритетами . Елементами цієї черги будуть пари (відстань до вершини від витоку, вершина). Таким чином, на вершині черги завжди знаходиться вершина графа з найменшою відстанню від витоку.

  priority_queue<edge> pq;
  pq.push(edge(start, 0));

Ініціалізуємо масив найкоротших відстаней . Вершини графа нумеруються з до .

  d = vector<int>(n + 1, INF);
  d[start] = 0;

Продовжуємо алгоритм Дейкстри, поки черга не порожня.

  while (!pq.empty())
  {

Витягуємо пару з вершини черги з пріоритетами.

    edge e = pq.top(); pq.pop();
    int v = e.node;

Перевіряємо, чи є вершина фіктивною. Якщо відстань до більша, ніж уже обчислене значення , то ігноруємо цю вершину.

    if (e.dist > d[v]) continue;

Перебираємо всі вершини , суміжні з . Відстань від до дорівнює .

    for (auto ed : g[v])
    {
      int to = ed.node;
      int cost = ed.dist;

Якщо ребро (v, to) релаксується, то перераховуємо значення і заносимо пару (d[to], to) в чергу.

      if (d[v] + cost < d[to])
      {
        d[to] = d[v] + cost;
        pq.push(edge(to, d[to]));
      }
    }
  }
}

Основна частина програми. Читаємо вхідні дані.

scanf("%d %d %d %d", &n, &m, &start, &fin);
g.resize(n + 1);
for (i = 0; i < m; i++)
{

Читаємо неорієнтоване ребро вагою і додаємо його до графа .

  scanf("%d %d %d", &b, &e, &w);
  g[b].push_back(edge(e, w));
  g[e].push_back(edge(b, w));
}

Запускаємо алгоритм Дейкстри з початкової вершини .

Dijkstra(g, dist, start);

Виводимо шуканий найкоротший шлях dist[fin]. Якщо dist[fin] дорівнює нескінченності, то шуканого шляху не існує.

if (dist[fin] == INF)
  printf("-1\n");
else
  printf("%d\n", dist[fin]);

Java реалізація

import java.util.*;

class Node implements Comparator<Node> 
{
  public int node, dist;
  
  Node() {}
  
  Node(int node, int dist)
  {
    this.node = node;
    this.dist = dist;
  }
  
  @Override
  public int compare(Node a, Node b) 
  { 
    return a.dist - b.dist; 
  }   
};


public class Main 
{
  // Adjacency list representation of the edges 
  static List<List<Node> > g;
  static int dist[];
  static int INF = Integer.MAX_VALUE;
  static int n, m; 
  
  static void dijkstra(int start)
  {
    PriorityQueue<Node> pq = new PriorityQueue<Node>(n+1, new Node());	  
    for (int i = 0; i <= n; i++) 
      dist[i] = Integer.MAX_VALUE;

    // Add source node to the priority queue 
    pq.add(new Node(start, 0)); 

    // Distance to the source is 0 
    dist[start] = 0;

    while(!pq.isEmpty())
    {
      Node e = pq.poll();
      int v = e.node;
      if (e.dist > dist[v]) continue;

      for(int j = 0; j < g.get(v).size(); j++)
      {
        int to = g.get(v).get(j).node;
        int cost = g.get(v).get(j).dist;
        if (dist[v] + cost < dist[to])
        {
          dist[to] = dist[v] + cost;
          pq.add(new Node(to,dist[to]));
        }
      }
    }
  }
  
  public static void main(String[] args) 
  {
    Scanner con = new Scanner(System.in);
    n = con.nextInt();
    m = con.nextInt();
    int start = con.nextInt();
    int fin = con.nextInt();

    g = new ArrayList<List<Node> >();
    // Initialize list for every node 
    for (int i = 0; i <= n; i++) 
    { 
      List<Node> item = new ArrayList<Node>(); 
      g.add(item); 
    }    
    
    for(int i = 0; i < m; i++)
    {
      int b = con.nextInt();
      int e = con.nextInt();
      int w = con.nextInt();

      g.get(b).add(new Node(e,w));
      g.get(e).add(new Node(b,w));
    }
    
    dist = new int[n+1];
    dijkstra(start); 
    
    if (dist[fin] == INF)
      System.out.println(-1);
    else
      System.out.println(dist[fin]);
    con.close();
  }
}

Python реалізація

Імпортуємо чергу з пріоритетом.

from queue import PriorityQueue

Оголосимо константу .

INF = 10**9

Функція Dijkstra реалізує алгоритм Дейкстри, починаючи з вершини .

def dijkstra(graph, dist, start):

Оголосимо та ініціалізуємо чергу з пріоритетами . Елементами цієї черги будуть пари (відстань до вершини від витоку, вершина). Таким чином, на вершині черги завжди знаходиться вершина графа з найменшою відстанню від витоку.

  pq = PriorityQueue()
  pq.put((0, start))

Ініціалізуємо список найкоротших відстаней . Вершини графа нумеруються з до .

  dist.extend([INF] * (n + 1))
  dist[start] = 0

Продовжуємо алгоритм Дейкстри, поки черга не порожня.

  while not pq.empty():

Витягуємо пару (cur_dist, v) з вершини черги з пріоритетами.

    cur_dist, v = pq.get()

Перевіряємо, чи є вершина фіктивною. Якщо відстань до більша, ніж уже обчислене значення dist[v], то ігноруємо цю вершину.

    if cur_dist > dist[v]: continue

Перебираємо всі вершини , суміжні з . Відстань від до дорівнює .

    for to, cost in graph[v]:

Якщо ребро (v, to) релаксується, то перераховуємо значення і заносимо пару (dist[to], to) в чергу.

      if dist[v] + cost < dist[to]:
        dist[to] = dist[v] + cost
        pq.put((dist[to], to))

Основна частина програми. Читаємо вхідні дані.

n, m = map(int, input().split())
start, fin = map(int, input().split())
g = [[] for _ in range(n + 1)]

for _ in range(m):

Читаємо неорієнтоване ребро вагою і додаємо його до графа .

  b, e, w = map(int, input().split())
  g[b].append((e, w))
  g[e].append((b, w))

Ініціалізуємо список найкоротших відстаней .

dist = []

Запускаємо алгоритм Дейкстри з початкової вершини .

dijkstra(g, dist, start)

Виводимо шуканий найкоротший шлях dist[fin]. Якщо dist[fin] дорівнює нескінченності, то шуканого шляху не існує.

if dist[fin] == INF:
  print("-1")
else:
  print(dist[fin])