Борьба с вычислением кратчайшего пути в графе со специальными весами ребер

Я работал над проблемой, когда мне нужно найти кратчайший путь в графе, учитывая два типа весов ребер: нормальный и специальный. Проверьте проблему здесь.

Я составил список смежности и использовал алгоритм Дейкстры, чтобы найти кратчайшее расстояние. Однако у меня возникают проблемы с получением правильных результатов для некоторых тестовых случаев.

Вот краткий обзор моего подхода:

  1. Представление графа: я использовал список смежности для хранения ребер. Каждое ребро имеет нормальный и специальный вес.
  2. Алгоритм Дейкстры: я реализовал алгоритм Дейкстры, используя очередь с приоритетом минимальной кучи. Я поддерживаю массив расстояний для хранения кратчайших расстояний, учитывая как обычные, так и специальные веса.

Несмотря на такой подход, я сталкиваюсь с проблемами, из-за которых расстояния не всегда рассчитываются правильно, а некоторые тестовые примеры завершаются неудачно. В частности, я считаю, что проблема заключается в том, как я обращаюсь со специальными весами в процессе релаксации кромок.

#include <bits/stdc++.h> 
using namespace std;

typedef pair<int, int> Pair;

struct Edge {
 int v;
 int normal;
 int special;
};

int findShortestDistance(int numberOfVertices, vector<vector<int>> &adj, int src, int dst) {
 vector<vector<Edge>> adjacencyList(numberOfVertices + 1);

 // Building the adjacency list
 for (int i = 0; i < adj.size(); i++) {
     adjacencyList[adj[i][0]].push_back({adj[i][1], adj[i][2], adj[i][3]});
 }

 // Min heap priority queue
 priority_queue<Pair, vector<Pair>, greater<Pair>> minHeap;
 vector<vector<int>> distanceFromSource(numberOfVertices + 1, vector<int>(2, INT_MAX));

 minHeap.push({0, src});
 distanceFromSource[src][0] = 0;

 while (!minHeap.empty()) {
     auto currentPair = minHeap.top();
     minHeap.pop();

     int currentNode = currentPair.second;
     int currentDistance = currentPair.first;

     // Exploring neighbors
     for (auto neighbor : adjacencyList[currentNode]) {
         int neighborNode = neighbor.v;
         int edgeWeight = neighbor.normal;
         int specialWeight = neighbor.special;

         // Relaxing the edge with normal weight
         if (distanceFromSource[neighborNode][0] > currentDistance + edgeWeight) {
             distanceFromSource[neighborNode][0] = currentDistance + edgeWeight;
             minHeap.push({distanceFromSource[neighborNode][0], neighborNode});
         }

         // Relaxing the edge with special weight
         if (distanceFromSource[neighborNode][1] > currentDistance + specialWeight) {
             distanceFromSource[neighborNode][1] = currentDistance + specialWeight;
             // minHeap.push({distanceFromSource[neighborNode][1], neighborNode});
         }
     }
 }

 return min(distanceFromSource[dst][0], distanceFromSource[dst][1]);
}

Не отмечайте это как dsa. Тег dsa предназначен для Digital Signature Algorithm.

PaulMcKenzie 23.07.2024 18:25
stackoverflow.com/questions/33948066/…
Matt Timmermans 23.07.2024 20:12

@MattTimmermans Описание короткое, но код, как правило, будет намного длиннее, чем код ОП, что уже почти правильно.

Unmitigated 24.07.2024 00:23

@Unmitigated, когда вы кодируете неявный новый граф вместо того, чтобы фактически создавать новый граф, изменения в стандартной версии Дейкстры на самом деле очень незначительны... может быть, даже точно такие же, как вы сделали. Я много раз отвечал на очень похожие вопросы, поэтому не склонен проверять слишком внимательно :)

Matt Timmermans 24.07.2024 00:40

@MattTimmermans Достаточно справедливо. Действительно, отказ от построения графа в явном виде должен тогда означать примерно то же самое.

Unmitigated 24.07.2024 00:41
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
5
51
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы используете currentDistance для расчета лучших путей для следующего узла, не принимая во внимание, включал ли этот путь особое ребро.

Не слишком сильно меняя код, вы можете вместо этого получить расстояния напрямую из вектора distanceFromSource.

Есть только один способ создать путь к соседу без использования специального ребра: использовать найденный на данный момент кратчайший путь к текущему узлу, который не использует специальное ребро, и соединить его с соседом через обычное ребро.

Для пути, использующего специальное ребро, вы можете либо использовать кратчайший путь к текущему узлу, который уже использует специальное ребро, и передать нормальное ребро к соседу, либо использовать самый короткий путь к текущему узлу, который не использует специальное ребро. а затем передать особое преимущество соседу.

С этими изменениями ваше решение пройдет:

std::vector<std::vector<int>> distanceFromSource(numberOfVertices + 1, std::vector<int>(2, 1e9));
// don't use INT_MAX as it results in overflow later

// inside the loop:
if (distanceFromSource[neighborNode][0] > distanceFromSource[currentNode][0] + edgeWeight) {
    minHeap.push({distanceFromSource[neighborNode][0] = distanceFromSource[currentNode][0] + edgeWeight, neighborNode});
}
int bestWithSpecial = std::min(distanceFromSource[currentNode][1] + edgeWeight, distanceFromSource[currentNode][0] + specialWeight);
if (distanceFromSource[neighborNode][1] > bestWithSpecial) {
    minHeap.push({
        distanceFromSource[neighborNode][1] = bestWithSpecial,
        neighborNode
    });
}

Обратите внимание, что это можно оптимизировать, чтобы избежать добавления ненужных узлов в очередь приоритетов.

Спасибо за подход. У меня была та же мысль, но я был обеспокоен тем, что если мы ослабим ее с помощью специального веса, путь может содержать более одного специального ребра. Теперь я понимаю интуицию.

Himanshu 24.07.2024 21:00

@Himanshu Рад помочь.

Unmitigated 24.07.2024 21:05

Другие вопросы по теме