PTA 旅游规划(C语言版本)思路+代码实现(Dijkstra 算法)

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);
}

欢迎大家批评指正

  • 11
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用邻接表实现Dijkstra算法求单源最短路径的C语言代码: ```c #include <stdio.h> #include <stdlib.h> #include <limits.h> #define MAX_VERTICES 1000 typedef struct Edge { int dest; int weight; struct Edge* next; } Edge; typedef struct Node { Edge* head; } Node; typedef struct Graph { int num_vertices; Node nodes[MAX_VERTICES]; } Graph; Graph* create_graph(int num_vertices) { Graph* graph = (Graph*) malloc(sizeof(Graph)); graph->num_vertices = num_vertices; for (int i = 0; i < num_vertices; i++) graph->nodes[i].head = NULL; return graph; } Edge* create_edge(int dest, int weight) { Edge* edge = (Edge*) malloc(sizeof(Edge)); edge->dest = dest; edge->weight = weight; edge->next = NULL; return edge; } void add_edge(Graph* graph, int src, int dest, int weight) { Edge* edge = create_edge(dest, weight); edge->next = graph->nodes[src].head; graph->nodes[src].head = edge; } int min_distance(int dist[], int visited[], int num_vertices) { int min_dist = INT_MAX, min_vertex; for (int i = 0; i < num_vertices; i++) { if (!visited[i] && dist[i] < min_dist) { min_dist = dist[i]; min_vertex = i; } } return min_vertex; } void print_solution(int dist[], int num_vertices) { printf("Vertex\tDistance from Source\n"); for (int i = 0; i < num_vertices; i++) printf("%d\t%d\n", i, dist[i]); } void dijkstra(Graph* graph, int src) { int dist[MAX_VERTICES]; int visited[MAX_VERTICES]; for (int i = 0; i < graph->num_vertices; i++) { dist[i] = INT_MAX; visited[i] = 0; } dist[src] = 0; for (int i = 0; i < graph->num_vertices - 1; i++) { int u = min_distance(dist, visited, graph->num_vertices); visited[u] = 1; for (Edge* edge = graph->nodes[u].head; edge != NULL; edge = edge->next) { int v = edge->dest; int weight = edge->weight; if (!visited[v] && dist[u] != INT_MAX && dist[u] + weight < dist[v]) dist[v] = dist[u] + weight; } } print_solution(dist, graph->num_vertices); } int main() { int num_vertices = 5; Graph* graph = create_graph(num_vertices); add_edge(graph, 0, 1, 10); add_edge(graph, 0, 4, 5); add_edge(graph, 1, 2, 1); add_edge(graph, 1, 4, 2); add_edge(graph, 2, 3, 4); add_edge(graph, 3, 2, 6); add_edge(graph, 3, 0, 7); add_edge(graph, 4, 1, 3); add_edge(graph, 4, 2, 9); dijkstra(graph, 0); return 0; } ``` 这里实现Dijkstra算法使用了邻接表来表示图,使用了堆排序的思想来寻找当前距离源点最近的未访问过的节点,以提高算法效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值