首先,我的图表包含负权重,所以我不能使用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
是边数,2
和3
分别是来源和目的地(0<=sourceNodes<=2
和3<=destinationsNodes<=5
)我必须计算最短路径。
所以,在这个输入文件中,我的代码给了我这个输出,如果我们看到我为你做的画,那就错了。
输出为:
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
,这是源节点之间距离最短的路径和目标节点(并且它是正确的)。
谢谢
答案 0 :(得分:1)
首先,如果存在负循环,那么我们无法找到最短路径。很容易想象出来。因为如果我们反复遍历负循环,那么每次遍历的成本都会降低。因此,我们会发现我们的道路价值无限下降。
嗯,为了避免这个缺点,我们使用Bellman-Ford的算法。它检测图表是否包含负循环。我假设您了解Bellman-Ford和Dijkstra的算法并习惯于使用术语&#34; Relaxation&#34;。
现在我们将采用称为约翰逊算法的方法:
以新的顶点X为源,我们将应用Bellman-Ford 算法。哪个会找到所有边缘的最短路径 总共(n-1)次迭代中的源,其中 n 是总数 顶点包括X. 我们将从同一个来源进行额外的迭代,并且在两种不同的情况下它会有不同的表现。
存在负循环:将再次看到放松。 这意味着存在负循环,我们无法做到最短 如上所述,图中的路径。所以,我们的计划应该 终止。
不存在负循环:不会发生松弛,我们从X获得所有顶点的最短路径。我们准备好了!
现在,来自所选来源的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;。