从源列表和目标列表

时间:2018-06-19 09:54:21

标签: java algorithm shortest-path

首先,我的图表包含负权重,所以我不能使用Dijkstra的算法。

我尝试使用和编辑一种Floyd-Warshall算法,但仅在某些情况下才有用。也许我必须使用贝尔曼 - 福特算法的编辑版本,但我无法找到方法..

<EDIT>

我无法找到获得正确输出的方法,因为我无法找到最短路径,因为我能够做到这一点,但要在此输入中输出正确的输出。 (参见绘图并将其与输出进行比较,您可以看到它是不同的。例如:

2 -> 5     -4    2 -> 1 -> 3 -> 4 -> 5

距离-4不正确,绘图为-2,而在另一个输出中,如下文所述,输入位不同,一切正确。

</EDIT>

这是我的输入(1)文件:

6 9
2 3
0 1 -2
0 2 1
2 1 -3
1 3 2
2 3 3
2 5 1
5 3 1
3 4 1
4 5 -3

6是节点数,9是边数,23分别是来源和目的地(0<=sourceNodes<=23<=destinationsNodes<=5)我必须计算最短路径。 所以,在这个输入文件中,我的代码给了我这个输出,如果我们看到我为你做的画,那就错了。

Graph from input 1

输出为:

pairs     dist     path
0 -> 3    -1     0 -> 1 -> 3
0 -> 4     0     0 -> 1 -> 3 -> 4
0 -> 5    -3     0 -> 1 -> 3 -> 4 -> 5
1 -> 3     1     1 -> 3
1 -> 4     2     1 -> 3 -> 4
1 -> 5    -1     1 -> 3 -> 4 -> 5
2 -> 3    -2     2 -> 1 -> 3
2 -> 4    -1     2 -> 1 -> 3 -> 4
2 -> 5    -4     2 -> 1 -> 3 -> 4 -> 5

这是我的代码:

import java.util.*;
import java.lang.*;
import java.io.*;

public class Esercizio3 {

public static void main(String args[]) throws IOException {
    try {
        Scanner scan = new Scanner(new File("/myFiles/Input2Es3.txt"));
        int totNodi = scan.nextInt();
        System.out.println("totNodi: "+totNodi);
        int totArchi = scan.nextInt();
        System.out.println("totArchi: "+totArchi);
        // ingressi
        int nIngressi = scan.nextInt();
        System.out.println("nIngressi: "+nIngressi);
        int[] ingresso = new int[nIngressi+1];
        for (int i=0; i<=nIngressi; i++) {
            ingresso[i] = i;
            System.out.println("> INGRESSO: "+ingresso[i]);
        }
        // uscite
        int startUscite = scan.nextInt();
        //        int endUscite = totNodi-1;
        int nUscite = totNodi-startUscite;
        System.out.println("nUscite: "+nUscite);
        int[] uscita = new int[nUscite];
        for (int i=startUscite; i<totNodi; i++) {
            int index = i-startUscite;
            uscita[index] = i;
            System.out.println("> USCITA: "+uscita[index]);
        }
        // archi
        int V = totNodi;
        int E = totArchi;
        int[][] weights = new int[totArchi][3];
        for (int i=0; i<totArchi; i++) {
            weights[i][0] = scan.nextInt();
            weights[i][1] = scan.nextInt();
            weights[i][2] = scan.nextInt();
            System.out.println(weights[i][0] + " - " + weights[i][1] + " - " + weights[i][2]);
        }

        floydWarshall(weights,totNodi,ingresso,uscita);


    } catch (FileNotFoundException ex) {
        System.out.println(ex);
    }
}

static void floydWarshall(int[][] weights, int numVertices, int[] ingresso, int[] uscita) throws IOException {

    double[][] dist = new double[numVertices][numVertices];
    for (double[] row : dist)
        Arrays.fill(row, Double.POSITIVE_INFINITY);

    for (int[] w : weights)
        dist[w[0]][w[1]] = w[2];

    int[][] next = new int[numVertices][numVertices];
    for (int i = 0; i < next.length; i++) {
        for (int j = 0; j < next.length; j++)
            if (i != j)
                next[i][j] = j + 1;
    }

    for (int k = 0; k < numVertices; k++)
        for (int i = 0; i < numVertices; i++)
            for (int j = 0; j < numVertices; j++)
                if (dist[i][k] + dist[k][j] < dist[i][j]) {
                    dist[i][j] = dist[i][k] + dist[k][j];
                    next[i][j] = next[i][k];
                }

    printResult(dist, next, ingresso, uscita);
}

static void printResult(double[][] dist, int[][] next, int[] ingresso, int[] uscita) throws IOException {
    BufferedWriter writer = new BufferedWriter(new FileWriter("myoutputfile.txt"));

    double distMin =  Double.POSITIVE_INFINITY;
    int indexI = 0;
    int indexJ = 0;
    for (int i = 0; i < next.length; i++) {
        for (int j = 0; j < next.length; j++) {
            if ((i != j) && (dist[i][j]!=Double.POSITIVE_INFINITY) && (i>=ingresso[0] && i<=ingresso[ingresso.length-1]) && (j>=uscita[0] && j<=uscita[uscita.length-1])) {
                int u = i + 1;
                int v = j + 1;
                String path = format("%d -> %d    %2d     %s", i, j, (int) dist[i][j], i);
                do {
                    u = next[u-1][v-1];
                    path += " -> " + (u-1);
                } while (u != v);
                System.out.println(path);



                if(distMin > dist[i][j]) {
                    distMin = dist[i][j];
                }

            }
        }
    }
}

}

我该如何解决这个问题?因为有了另一个输入它完美运行:

输入(2)运行(它与第一个相似,但在最后一个原始中的权重不同)

6 9
2 3
0 1 -2
0 2 1
2 1 -3
1 3 2
2 3 3
2 5 1
5 3 1
3 4 1
4 5 1

输出完美:

0 -> 3     0     0 -> 1 -> 3
0 -> 4     1     0 -> 1 -> 3 -> 4
0 -> 5     2     0 -> 2 -> 5
1 -> 3     2     1 -> 3
1 -> 4     3     1 -> 3 -> 4
1 -> 5     4     1 -> 3 -> 4 -> 5
2 -> 3    -1     2 -> 1 -> 3
2 -> 4     0     2 -> 1 -> 3 -> 4
2 -> 5     1     2 -> 5

我唯一知道的是,对于第一个输入,输出应该是-1,而对于最后一个输入,输出应该是2 -> 1 -> 3,这是源节点之间距离最短的路径和目标节点(并且它是正确的)。

谢谢

3 个答案:

答案 0 :(得分:1)

首先,如果存在负循环,那么我们无法找到最短路径。很容易想象出来。因为如果我们反复遍历负循环,那么每次遍历的成本都会降低。因此,我们会发现我们的道路价值无限下降。

嗯,为了避免这个缺点,我们使用Bellman-Ford的算法。它检测图表是否包含负循环。我假设您了解Bellman-Ford和Dijkstra的算法并习惯于使用术语&#34; Relaxation&#34;。

现在我们将采用称为约翰逊算法的方法:

  • 我们将添加一个额外的顶点X并将其连接到图形的所有其他顶点,并且边缘都将为0。
  • 以新的顶点X为源,我们将应用Bellman-Ford 算法。哪个会找到所有边缘的最短路径 总共(n-1)次迭代中的源,其中 n 是总数 顶点包括X. 我们将从同一个来源进行额外的迭代,并且在两种不同的情况下它会有不同的表现。

    1. 存在负循环:将再次看到放松。 这意味着存在负循环,我们无法做到最短 如上所述,图中的路径。所以,我们的计划应该 终止。

    2. 不存在负循环:不会发生松弛,我们从X获得所有顶点的最短路径。我们准备好了!

  • 我们将使用Bellman-Ford算法的最短路径重新加权原始图形的边缘。 如果 u v 在主图中有成本 w(u,v)并且 u 和 v 分别是 h(u) h(v),然后新的权重 nw(u,v) = w(u,v)+ h(u)-h(v)

现在,来自所选来源的Dijkstra算法应该找到重新加权图上所有顶点的最短路径,这也是原始图的最短路径。

如果您感到困惑,请在维基百科上查看Johnson's algorithm

答案 1 :(得分:0)

您是否希望尽可能减少路线的总成本(-6的结果优于-3)或尽可能接近0(-3优于-6)? 如果你试图接近0,我认为这实际上可能是NP-hard问题,没有合理的算法,这将给你100%正确的答案。这些问题通常可以通过一些启发式算法来解决,这些算法可以为您提供足够好的结果,或者如果节点网络不是那么大,您可以通过选择最佳算法来进行枚举(将此网络编写为线性{{3在php,java ......中有一些可用的解算器。

答案 2 :(得分:0)

我认为你可以使用改编的Dijkstra。如果您在该周期内检测到导致否定结果的周期,则将路径标记为&#34; undefined&#34;,因为最短路径为&#34;无限短路&#34;。