Loading

<数据结构>XDOJ327.最短路径

问题与解答

问题描述
求图中任意两个顶点之间的最短路径。
输入格式
输入数据第一行是一个正整数,表示图中的顶点个数n(顶点将分别按0,1,…,n-1进行编号)。之后的n行每行都包含n个整数,第i行第j个数表示顶点i-1和顶点j-1之间的边长,用10000来表示两个顶点之间无边。后面每行2个数字,表示一对待求最短路径的顶点,用-1 -1表示输入结束,-1 -1不求解。
输出格式
每对待求最短路径的顶点输出两行数据:第一行输出两个顶点间的最短路径长度,第二行输出最短路径,要按顺序输出顶点编号序列,顶点间用空格隔开。当两个顶点间没有路径时,只在一行上输出字符串“NO”。
样例输入
7
0 12 10000 10000 10000 10000 10000
12 0 10000 10000 3 10000 10000
10000 10000 0 10000 10000 21 11
10000 10000 10000 0 10000 10000 10000
10000 3 10000 10000 0 10000 8
10000 10000 21 10000 10000 0 10000
10000 10000 11 10000 8 10000 0
0 2
0 3
5 0
2 1
1 5
-1 -1
样例输出
34
0 1 4 6 2
NO
55
5 2 6 4 1 0
22
2 6 4 1
43
1 4 6 2 5

//最短路径
//求出所给两点之间的最短路径长度 并 输出最短路径
// Dijstra算法
#include<stdio.h>
#include<algorithm>
#include<stack>
using namespace std;
#define MaxN 100
#define INF 10000  //G[i][j] == INF表示i,j之间无边
int n;  //顶点数
int G[MaxN][MaxN];  //邻接矩阵表示图
int d[MaxN];  //d[i]表示i到起点的距离
int path[MaxN];  //存储最短路径
bool Vis[MaxN] = {false};  //Vis[i]==true表示i已被访问
int Dijstra(int u, int v);  //Dijstra算法
void PrintPath(int start, int end);  //输出路径

int main(){
    int start,end,Min_Length;
    int i,j;
    scanf("%d",&n);  //输入顶点
    for(i = 0; i < n; i++){  //输入图的邻接矩阵
        for(j = 0; j < n; j++)
            scanf("%d",&G[i][j]);
    }
    while(1){   //多点测试
        scanf("%d%d",&start,&end);
        if(start == -1 && end == -1) return 0;  //结束程序
        Min_Length = Dijstra(start, end);  //调用Dijstra算法
        if(Min_Length == -1) printf("No\n");  //打印结果
        else{
            printf("%d\n", Min_Length);
            PrintPath(start,end);
        }
    }
}

int Dijstra(int start, int end){
    fill(d, d+n, INF);   //初始化各数组
    d[start] = 0;
    fill(Vis,Vis+n,false);
    path[start] = start;

    int i,u,v,Min;
    for(i = 0; i < n; i++){  //循环n次
        u = -1, Min = INF;
        for(v = 0; v < n; v++){  //寻找未被访问的,距离起点距离最短的点
            if(Vis[v] == false && d[v] < Min){
                u = v;
                Min = d[v];
            }
        }
        if(u == -1) break;  //图不连通,中断循环
        Vis[u] = true;  //标记已访问的点
        for(v = 0; v < n; v++){  //以u为中介点对其未被访问的邻接点进行优化
            if(G[u][v] != INF && Vis[v] == false 
                && d[u] + G[u][v] < d[v])
            {
                d[v] = d[u] + G[u][v];
                path[v] = u;  //v的父(前驱)节点是u
            }
        }
    }
    if(d[end] == INF) return -1;    //start与end不连通,不存在最短路径
    else              return d[end];//返回最短路径的长度
}

void PrintPath(int start, int end){  //输出最短路径
    stack<int> s;
    int i = end;
    s.push(end);  //终点入栈
    while(path[i] != i){
        s.push(path[i]);  //父(前驱)节点入栈
        i = path[i];
    }
    while(!s.empty()){   //输出
        printf("%d ",s.top());
        s.pop();
    }
    printf("\n");
}

题后反思:调试记录

程序出错

忽略了Vis数组:

  • 与起点距离最短的点必须是“未被访问过的”
  • 而后要标记为“已访问”
  • 需要更新的邻接点必须是“未被访问过的”

在源代码基础上的改动

源代码:Dijstra算法

增加输入变量并改变有顶点不连通时的行为

  • 源代码输入起点start,计算start到其他所有顶点的最短距离,若有顶点不连通则函数返回-1
  • 改动后
    • 输入起点start 和 终点end, 若有顶点不连通则中断for循环
    • 根据d[end]是否==INF判断输出-1还是d[end](start与end的最小值)

增加path[]数组记录路径【父节点树/并查集】

  • path[v] = u表示 v的父节点是u
  • 沿path遍历父节点,入栈再出栈得到最短路径
posted @ 2021-12-21 16:59  咪啪魔女  阅读(330)  评论(0编辑  收藏  举报