Флойд-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;

}