《信息学奥赛一本通 (C++)版》最短路径问题

116 阅读2分钟

题目出处:《[信息学奥赛一本通](C++)版》P472页

线上OJ:ybt.ssoier.cn:8088/problem_sho…

本题在书中作为 Floyed 问题,估此处采用 Floyd 解法。Floyed的时间复杂度是O (N3),适用于出现负边权的情况

核心思想是三层循环,

第一层循环中间点 k,

第二层循环起点 i,

第三层循环终点 j,

算法的思想很容易理解:如果点 i 到点 k 的距离加上点 k 到点 j 的距离小于原先点 i 到点 j 的距离,那么就用这个更短的路径长度来更新原先点 i 到点 j 的距离,用代码表示就是

If (res[i][j] >res[i][k] + res[k][j]) res[i][j] = res[i][k] + res[k][j];

注:在初始化时有个小技巧

如果是 int 数组,memset(g, 0x3f, sizeof(g)) 可全部初始化为一个很大的数

如果是 double 数组,memset(g,127,sizeof(g)) 可全部初始化为一个很大的数

题解代码:

#include <bits/stdc++.h>
using namespace std;
 
int n, m;
 
int main()
{
    scanf("%d", &n);
    int x[n + 1], y[n + 1];
    double res[n + 1][n + 1];

    memset(res, 127, sizeof res); // res初始化为一个很大值 
	
    for (int i = 1; i <= n; i ++ )
        scanf("%d%d", &x[i], &y[i]);

    scanf("%d", &m);
    for (int i = 1; i <= m; i ++ )
    {
        int a1, b1;
        scanf("%d%d", &a1, &b1);// 使用距离公式,每次读入时计算距离
        res[a1][b1] = res[b1][a1] = sqrt(pow(double(x[a1] - x[b1]), 2) + pow(double(y[a1] - y[b1]), 2));
    }
	
    int s, t;
    scanf("%d%d", &s, &t);

    for (int k = 1; k <= n; k ++ )// Floyd三重循环
        for (int i = 1; i <= n; i ++ )
            for (int j = 1; j <= n; j ++ )
            {
                if ((i != j) && (i != k) && (k != j))// 如果绕路走更近,就绕路
                    res[i][j] = min(res[i][j], res[i][k] + res[k][j]);
            }
			
    printf("%.02lf\n", res[s][t]);
    return 0;
}