7-9 旅游规划
分数 25
作者 陈越
单位 浙江大学
有了一张自驾旅游路线图,你会知道城市间的高速公路长度、以及该公路要收取的过路费。现在需要你写一个程序,帮助前来咨询的游客找一条出发地和目的地之间的最短路径。如果有若干条路径都是最短的,那么需要输出最便宜的一条路径。
输入格式:
输入说明:输入数据的第1行给出4个正整数N、M、S、D,其中N(2≤N≤500)是城市的个数,顺便假设城市的编号为0~(N−1);M是高速公路的条数;S是出发地的城市编号;D是目的地的城市编号。随后的M行中,每行给出一条高速公路的信息,分别是:城市1、城市2、高速公路长度、收费额,中间用空格分开,数字均为整数且不超过500。输入保证解的存在。
输出格式:
在一行里输出路径的长度和收费总额,数字间以空格分隔,输出结尾不能有多余空格。
输入样例:
4 5 0 3
0 1 1 20
1 3 2 30
0 3 4 10
0 2 2 20
2 3 1 20
输出样例:
3 40
代码长度限制
16 KB
Java (javac)
时间限制
800 ms
内存限制
64 MB
其他编译器
时间限制
400 ms
内存限制
64 MB
这道题考察的是给定的起点到终点的最短路径问题,即单源最短路径问题,我们用Dijkstra 算法来实现。
这里对Dijkstra 算法的思想不再过多描述,重点会放在具体的代码实现上。(如果对思想不太熟悉的同学可以先去看看懒猫老师的视频,老师视频里讲的也很清晰,原理和伪代码的思路都讲的很好,我这里也是根据懒猫老师的讲解来完成对代码的实现)
首先我们需要一些辅助数组来完成Dijkstra 算法的实现
1. int s [ ]
用来标志对应的顶点(这里说的对应顶点的意思是顶点序号与数组下标对应,即s [ 0 ]代表的顶点0,以下所说的对应顶点都是相同的意思)是否已经找到从目标顶点出发的最短路径;1代表已经找到,0代表还没有找到。
2.edge dist [ ]
用来记录当前从目标顶点出发到该顶点的最短路径。此处数组类型为edge 因为题目中有一个要求:如果有若干条路径都是最短的,那么需要输出最便宜的一条路径。我们需要在更新dist数组的时候注意当路径长度相同时,我们比较路径的费用。如果题干中没有这个要求,我们是可以将dist数组的类型直接定义为int类型的。
typedef struct edge{
int length;
int price;
}edge;
3.path [ ]
path数组是用来记录目标顶点到对应的顶点的最小路径中,对应顶点的上一个顶点的序号。
即 目标顶点是0,对应顶点是3,我们找到一条0->1->2->3 这样一条最短的从0到3的路径,那么path[ 3 ]=2。这道题实际上没有用到path数组,但是path数组是Dijkstra 算法实现的重要一环,我在代码中也体现出来了。
下面是对图的处理
定义图的结构体:
typedef struct MGragh{
int vertexnum;//顶点个数
edge arc[501][501];//邻接矩阵
}MGragh;
对图进行初始化
void init_MGragh(MGragh *G,int n,int m){
G->vertexnum=n;
for(int i=0;i<G->vertexnum;i++){//对矩阵初始化
for(int j=0;j<G->vertexnum;j++){
if(j!=i){
G->arc[i][j].length=999;
}//题目中说明最大长度不会超过500,我们可以将999视作无限大
else{
G->arc[i][j].length=0;
}//将对角线置为0;
}
}
int a,b,c,d;
for(int j=0;j<m;j++){
scanf("%d %d %d %d",&a,&b,&c,&d);//将有效边记录
G->arc[a][b].length=c; //注意无向图的邻接矩阵是对称的
G->arc[b][a].length=c;
G->arc[a][b].price=d;
G->arc[b][a].price=d;
}
}
总代码:
#include<stdio.h>
typedef struct edge{
int length;
int price;
}edge;
typedef struct MGragh{
int vertexnum;
edge arc[501][501];
}MGragh;
int s[501];
edge dist[501];
int path[501];
void init_MGragh(MGragh *G,int n,int m){
G->vertexnum=n;
for(int i=0;i<G->vertexnum;i++){//对矩阵初始化
for(int j=0;j<G->vertexnum;j++){
if(j!=i){
G->arc[i][j].length=999;
}//题目中说明最大长度不会超过500,我们将999视为无限大
else{
G->arc[i][j].length=0;
}//将对角线元素设置为0
}
}
int a,b,c,d;
for(int j=0;j<m;j++){
scanf("%d %d %d %d",&a,&b,&c,&d);//将有效边记录
G->arc[a][b].length=c; //注意无向图的邻接矩阵是对称的
G->arc[b][a].length=c;
G->arc[a][b].price=d;
G->arc[b][a].price=d;
}
}
int findMinDist(edge dist[],int s[],int n){
int min=999;
int flag;//记录最小的点的下标
for(int i=0;i<n;i++){
if(s[i]==0){
if(dist[i].length<min){
min=dist[i].length;
flag=i;
}
}
}
return flag;//返回最小顶点的下标
}//找到dist数组中最小的顶点的下标,如果有多个最短路径,我们就取序号靠前的顶点,而不需要考虑价格
//因为此处dist数组代表的是不同的点的最短路径,不同的点对比价格在此处毫无意义。
void djs(MGragh G,int startV){
for(int i=0;i<G.vertexnum;i++){//初始化集合s
s[i]=0;
}
s[startV]=1;//将初始顶点设置为1 //1代表已经存在最短路径
for(int i=0;i<G.vertexnum;i++){//初始化dist和path数组
dist[i].length=G.arc[startV][i].length;
dist[i].price=G.arc[startV][i].price;
if(dist[i].length!=999){
path[i]=startV;
}
else{
path[i]=-1;
}
}
int cnt=1;//计数器 已经找到最短路径的顶点的个数
while(cnt<G.vertexnum){
int min=findMinDist(dist,s,G.vertexnum);
s[min]=1;//将当前dist中最小的顶点加入到s中
for(int i=0;i<G.vertexnum;i++){//更新dist和path数组
if((s[i]==0) && (dist[i].length>(dist[min].length+G.arc[min][i].length))){
dist[i].length=dist[min].length+G.arc[min][i].length;
dist[i].price=dist[min].price+G.arc[min][i].price;
path[i]=min;
}//利用路径长度更新dist数组
else if((s[i]==0)&&(dist[i].length==(dist[min].length+G.arc[min][i].length))&&(dist[i].price>(dist[min].price+G.arc[min][i].price))){
dist[i].length=dist[min].length+G.arc[min][i].length;
dist[i].price=dist[min].price+G.arc[min][i].price;
path[i]=min;
}//如果路径等长,就利用价格更新dist数组
}
cnt++;
}
}
void _print(int terminalV){
printf("%d %d",dist[terminalV].length,dist[terminalV].price);
}//输出结果
int main()
{
int n;
int m;
int startV;
int terminalV;
scanf("%d %d %d %d",&n,&m,&startV,&terminalV);
MGragh G;//因为邻接矩阵数组较大,我们可以将G作为全局变量放在main函数外面;
init_MGragh(&G,n,m);//对图初始化
djs(G,startV);//Dijkstra的实现
_print(terminalV);
}
欢迎大家批评指正