算法分析
(y总真言,简单易懂)
-
f[i, j, k]
表示从i
走到j
的路径上除i
和j
点外只经过1
到k
的点的所有路径的最短距离。那么f[i, j, k] = min(f[i, j, k - 1), f[i, k, k - 1] + f[k, j, k - 1]
。
因此在计算第k
层的f[i, j]
的时候必须先将第k - 1
层的所有状态计算出来,所以需要把k
放在最外层。 -
读入邻接矩阵,将次通过动态规划装换成从
i
到j
的最短距离矩阵 -
在下面代码中,判断从
a
到b
是否是无穷大距离时,需要进行if(t > INF/2)
判断,而并非是if(t == INF)
判断,原因是INF
是一个确定的值,并非真正的无穷大,会随着其他数值而受到影响,t
大于某个与INF
相同数量级的数即可
时间复杂度 $O(n^3)$
Java 代码
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Main {
static int n;
static int m;
static int q;
static int N = 210;
static int INF = 0x3f3f3f3f;
static int[][] d = new int[N][N];
public static void floyd()
{
for(int k = 1;k <= n;k++)
for(int i = 1;i <= n;i++)
for(int j = 1;j <= n;j++)
d[i][j] = Math.min(d[i][j], d[i][k] + d[k][j]);
}
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String[] str1 = reader.readLine().split(" ");
n = Integer.parseInt(str1[0]);
m = Integer.parseInt(str1[1]);
q = Integer.parseInt(str1[2]);
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= n;j++)
{
if(i == j) d[i][j] = 0;
else d[i][j] = INF;
}
}
while(m -- > 0)
{
String[] str2 = reader.readLine().split(" ");
int a = Integer.parseInt(str2[0]);
int b = Integer.parseInt(str2[1]);
int c = Integer.parseInt(str2[2]);
d[a][b] = Math.min(d[a][b], c);//若有重边选择短的边
}
floyd();
while(q -- > 0)
{
String[] str3 = reader.readLine().split(" ");
int a = Integer.parseInt(str3[0]);
int b = Integer.parseInt(str3[1]);
int t = d[a][b];
if(t > INF / 2) System.out.println("impossible");
else System.out.println(t);
}
}
}
为什么要大于0x3f/2啊,被影响了不说明他们之间有最短路嘛
因为有负边权。
这里所有点都会计算,不是只有和起点有路的才会计算。
有负权边,比如当INF+(-2)<INF时也会更新,此时还没有计算出最短路
我也有同样的疑问后来弄明白了 :
g[i][j]=min(g[i][j],g[i][k]+g[k][j]),这里假设节点i没法到达节点k和节点j即g[i][k]=0x3f、g[i][j]=0x3f而且g[k][j]为负边,那么g[i][j]还是会被更新为0x3f加上g[k][j]这条负边的长度
写的真好,这才是dp分析的思路
感谢大佬
厉害