描述
一张地图包括n个城市,假设城市间有m条路径(有向图),每条路径的长度已知。给定地图的一个起点城市和终点城市,利用Dijsktra算法求出起点到终点之间的最短路径。
输入
多组数据,每组数据有m+3行。第一行为两个整数n和m,分别代表城市个数n和路径条数m。第二行有n个字符,代表每个城市的名字。第三行到第m+2行每行有两个字符a和b和一个整数d,代表从城市a到城市b有一条距离为d的路。最后一行为两个字符,代表待求最短路径的城市起点和终点。当n和m都等于0时,输入结束。
输出
每组数据输出两行。第一行为一个整数,为从起点到终点之间最短路的长度。第二行为一串字符串,代表该路径。每两个字符之间用空格隔开。
输入样例 1
3 3 A B C A B 1 B C 1 A C 3 A C 6 8 A B C D E F A F 100 A E 30 A C 10 B C 5 C D 50 E D 20 E F 60 D F 10 A F 0 0
输出样例 1
2 A B C 60 A E D F
//基于Dijsktra算法的最短路径求解
#include <iostream>
#define MaxInt 32767 //最长长度(无穷大)
#define MVNum 100 //最大定点数
using namespace std;
typedef struct{
char vexs[MVNum]; //定点集
int arcs[MVNum][MVNum]; //边集
int vexum,arcnum; //当前定点数量和边数量
}AMGraph;
void Create_Graph(AMGraph &G,int m,int n){//创建树,输入图、顶点数、边数
int arc;//边
G.vexum=m;//最大顶点数量等于m
G.arcnum=n;//最大边数量等于n
for(int i=1;i<=m;i++) cin>>G.vexs[i-1];//输入顶点符号
for(int i=1;i<=m;i++)
for(int j=1;j<=m;j++)
G.arcs[i-1][j-1]=MaxInt; //初始化每一条边
for(int i=1;i<=n;i++){
int begin=0,end=0;//边的起点终点位置初始化 l、r是逻辑地址
char bbegin,eend;//边的起点终点字符
cin>>bbegin>>eend;
for(int i=1;i<=m;i++)//查找起点字符所在的位置
if(G.vexs[i-1]==bbegin){begin=i;break;}
for(int i=1;i<=m;i++)//查找终点字符所在的位置
if(G.vexs[i-1]==eend){end=i;break;}
cin>>G.arcs[begin-1][end-1];//输入该边的长度
}
}
void Get_Way(AMGraph G,int Path[],int temp){//递归输出最短路径(由于本题使用的是前驱法,所以正常输出只能从后往前,为了从前往后。只能使用栈、递归、逆序输出的方式,我这里使用递归)
if(Path[temp-1]==0){//如果读到起点了 起点的D是0 这时候直接输出起点
cout<<G.vexs[temp-1];
return;
}
Get_Way(G,Path,Path[temp-1]);//递归寻找D前驱的路径
cout<<" "<<G.vexs[temp-1];//输出此刻的结点
}
void Dijsktra(AMGraph G,char bbegin,char eend){//进行一次Dijsktra算法不改变图本身,输入图、起点、终点
int begin=0,end=0;//边的起点终点位置初始化 l、r是逻辑地址
for(int i=1;i<=G.vexum;i++)//查找起点字符所在的位置
if(G.vexs[i-1]==bbegin){begin=i;break;}
for(int i=1;i<=G.vexum;i++)//查找终点字符所在的位置
if(G.vexs[i-1]==eend){end=i;break;}
int V[G.vexum];//用于表示每个点的最短路径是否已经确认
int D[G.vexum];//用于存储每个点到起点的距离
int Path[G.vexum];//用于存储每个点的前驱(逻辑地址),0表示无前驱
V[begin-1]=1;//起点入V集合
for(int i=1;i<=G.vexum;i++){
V[i-1]=0;
D[i-1]=G.arcs[begin-1][i-1];
Path[i-1]=begin;//所有点的前驱是起点 这里采用逻辑地址 0表示无前驱
}
D[begin-1]=0;//起点到自己的距离为0
Path[begin-1]=0;//起点的前驱改为0 这里采用逻辑地址 0表示无前驱
for(int i=1;i<=G.vexum;i++){//最多进行点数次Dijsktra运算
int min=MaxInt;//min表示一轮Dijsktra中G-V集合中距离起点最短的距离
int v=0;//一轮Dijsktra中G-V集合中距离起点最小的点的逻辑地址,0不存在
for(int j=1;j<=G.vexum;j++)//找出G-V集合中到起点最短的点
if(D[j-1]<min&&!V[j-1]){
min=D[j-1];
v=j;
}
V[v-1]=1;//G-V集合中距离起点最小的点入集合V
if(v==end){//如果这个点恰好是终点,直接输出,省略后面的Dijsktra运算
cout<<D[v-1]<<endl;
Get_Way(G,Path,end); //递归输出路径
cout<<endl; //输出换行符
break;
}
for(int j=1;j<=G.vexum;j++)//对以距离起点最短点为出度的边的入度点进行距离更新
if(D[v-1]+G.arcs[v-1][j-1]<D[j-1]){
D[j-1]=D[v-1]+G.arcs[v-1][j-1];
Path[j-1]=v;
}
}
}
void Calculate(int m,int n){//m定点数,n边数
AMGraph G;
char begin,end;
Create_Graph(G,m,n);//创建树
cin>>begin>>end;
Dijsktra(G,begin,end);//求最短路径
}
int main(){
int m,n;
while(cin>>m>>n&&m!=0&&n!=0){//每次处理一组数据
Calculate(m,n);
}
return 0;
}