<数据结构>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遍历父节点,入栈再出栈得到最短路径