Я работал над проблемой, когда мне нужно найти кратчайший путь в графе, учитывая два типа весов ребер: нормальный и специальный. Проверьте проблему здесь.
Я составил список смежности и использовал алгоритм Дейкстры, чтобы найти кратчайшее расстояние. Однако у меня возникают проблемы с получением правильных результатов для некоторых тестовых случаев.
Вот краткий обзор моего подхода:
Несмотря на такой подход, я сталкиваюсь с проблемами, из-за которых расстояния не всегда рассчитываются правильно, а некоторые тестовые примеры завершаются неудачно. В частности, я считаю, что проблема заключается в том, как я обращаюсь со специальными весами в процессе релаксации кромок.
#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]);
}
@MattTimmermans Описание короткое, но код, как правило, будет намного длиннее, чем код ОП, что уже почти правильно.
@Unmitigated, когда вы кодируете неявный новый граф вместо того, чтобы фактически создавать новый граф, изменения в стандартной версии Дейкстры на самом деле очень незначительны... может быть, даже точно такие же, как вы сделали. Я много раз отвечал на очень похожие вопросы, поэтому не склонен проверять слишком внимательно :)
@MattTimmermans Достаточно справедливо. Действительно, отказ от построения графа в явном виде должен тогда означать примерно то же самое.
Вы используете 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 Рад помочь.
Не отмечайте это как
dsa
. Тегdsa
предназначен дляDigital Signature Algorithm
.