Флойд-Warshall с отрицательными циклами. Как найти все неопределенные пути?
я реализовал алгоритм Флойда Warshall и он работает, но проблема в том, что я не знаю, как я могу найти все пути, которые не являются определенными. Я искал в интернете, но я могу найти только ответы на то, как определить, имеет ли график отрицательные циклы или нет.
vector< vector <int> > floyd_warshall(vector< vector<int> > d, int n){
for(int i = 0; i < n; i++) d[i][i] = 0;
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
for(int k = 0; k < n; k++){
if(d[j][i] + d[i][k] < d[j][k] and d[j][i] != INF and d[i][k] != INF){
d[j][k] = d[j][i] + d[i][k];
}
}
}
}
return d;
}
после запуска алгоритма на графике:
from: to: weight:
0 1 1
1 2 -1
2 1 -1
1 3 1
4 0 1
Я получаю матрицу смежности:
| 0 1 2 3 4
--|----------------------------
0 | 0 -1 -2 -2 INF
1 | INF -2 -3 -3 INF
2 | INF -3 -4 -4 INF
3 | INF INF INF 0 INF
4 | 1 -2 -3 -7 0
Я знаю, что если узел I является частью отрицательного цикла имеет отрицательное значение в положении d[i] [i] в матрице. Поэтому, если я проверю диагональ матрицы, я смогу найти все узлы, которые являются частью отрицательного цикла. Поэтому, если мы посмотрим в приведенном выше примере, мы увидим, что узлы 1 и 2 являются частями отрицательного цикла. Дело в том, что я хочу найти, какие пути определены, а какие-нет. Если вы можете перейти от A к B через отрицательный цикл, то длина пути должна быть неопределенной, так как она может быть произвольно малой.
Итак, вопрос в том, как могу ли я найти все неопределенные пути?
Я хочу, чтобы алгоритм вернул матрицу: (вместо приведенной выше)
| 0 1 2 3 4
--|----------------------------
0 | 0 -INF -INF -INF INF
1 | INF -INF -INF -INF INF
2 | INF -INF -INF -INF INF
3 | INF INF INF 0 INF
4 | 1 -INF -INF -INF 0
где d[i][j] = INF означает, что нет пути между i и j, а-INF означает, что существует произвольный малый путь между i и j (путь проходит где-то отрицательный цикл), а в противном случае d[i][j] самая короткая длина между i и j.
Я думал проверить каждый путь, но это, вероятно, будет слишком медленно. Должно быть какой стандартный способ решить эту проблему, верно?
спасибо
3 ответов
Ну, я нашел решение своей проблемы.
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++) // Go through all possible sources and targets
for(int k = 0; d[i][j] != -INF && k < n; k++)
if( d[i][k] != INF && // Is there any path from i to k?
d[k][j] != INF && // Is there any path from k to j?
d[k][k] < 0) // Is k part of a negative loop?
d[i][j] = -INF; // If all the above are true
// then the path from i to k is undefined
Я думаю, что это должно работать и, похоже, это тоже работа.
алгоритм Floyd-Warshall алгоритм выдает правильный результат пока никакой негативной циклы существуют во входном графике. В случае, если существует отрицательный цикл, вычисление кратчайшего (простого) пути является NP-трудной задачей и Алгоритм Floyd-Warshall не выводит правильный результат.
но можно обнаружить наличие отрицательного цикла, проверив, что есть отрицательная запись в диагонали матрицы. Это делается в строке 8 и строке 9 этого алгоритм:
1. M[i, j] := ∞ ∀i 6= j
2. M[i, i] := 0 ∀i
3. M[i, j] := c((i, j)) ∀(i, j) ∈ E(G)
4. for i := 1 to n do
5. for j := 1 to n do
6. for k := 1 to n do
7. if M[j, k] > M[j, i] + M[i, k]
then M[j, k] := M[j, i] + M[i, k]
8. for i := 1 to n do
9. if M[i, i] < 0 then return(’graph contains a negative cycle’)
У меня есть видео это объясняет, как именно работает алгоритм Флойда-Уоршелла. По сути, алгоритм Флойда-Уоршелла используется для поиска кратчайших путей между всеми парами узлов в взвешенном графе с положительным или отрицательным краевыми весами.
следующий алгоритм принимает матрицу смежности, где Double.POSITIVE_INFINITY используется, чтобы указать, что два узла не соединяются. Для каждого узла также вы, вероятно, захотите инициализировать вес от 0 до себя.
этот метод обновляет начальную матрицу, чтобы указать минимальное расстояние между всеми парами узлов. Если кратчайший путь сколь угодно мал,то ответ сохраняется как двойной.NEGATIVE_INFINITY. Если два узла не могут достичь друг друга, то он по-прежнему двойной.ПОЛОЖИТЕЛЬНАЯ БЕСКОНЕЧНОСТЬ. Эта реализация запускает Floyd Warshall дважды, и если длина пути меньше, чем раньше, то мы находимся в отрицательном цикле.
static void floydWarshall(double[][] dist) {
int n = dist.length;
// Compute shortest paths
for (int k = 0; k < n; k++)
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
if (dist[i][k] + dist[k][j] < dist[i][j])
dist[i][j] = dist[i][k] + dist[k][j];
// Identify negative cycles
for (int k = 0; k < n; k++)
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
if (dist[i][k] + dist[k][j] < dist[i][j])
dist[i][j] = Double.NEGATIVE_INFINITY;
}