4、地铁搭乘方案选择
轨道交通越来越发达,我们的出行也越来越方便。从学校门口的地铁站乘坐地铁可以到达很多地方。 计算地铁出行的最短路径,要求如下:(默认站点与站点之间权值为 1,也可以用时间或距离进行衡量)
1) 画出从西南石油大学出发,前往世纪城的地铁交通图,分析出哪条线路最短,详细说明分析思路。
2) 用算法实现你的分析思路,并输出最短路径。(包括代码和运行结果的截图)
3) 分析算法的性能,至少包括时间复杂度和空间复杂度等。 

成都市地铁路线图可以看作是一个有环无向图。
根据老师提供的成都地铁路线图,我一共划分了5条可行路线,并沿途标记了56个站点,相当于无向图中的56个结点。
根据题目要求我将默认站点与站点之间权值为1。

下面是我使用 ipadipadipad 画出的从西南石油大学出发,前往世纪城的地铁交通路线图。

1)思路分析

对于最短路问题,我这里使用 DijkstraDijkstraDijkstra 算法来求得从石油大学站到世纪城站的最短路线并使用prev数组记录最短路径并最后输出所选择路线的站点英文名称。

1. 求得最短路径算法思路分析:

本题实际上可以简化为:

题目背景

由于疫情原因我们没办法开学啦,为了能让同学们不挂科,心善的老师决定布置大作业作为期末成绩的一部分啦

题目描述

一个有环无向图中,有n个结点和m条边,每条边的权值都是1,求得从起点s到终点t的最短路长度,并正序输出最短路径信息。

输入格式

第一行两个正整数,n和m,表示图的结点数和边数。
第二行到n+1行,为n个字符串,代表n个站点信息(中间可能带空格的英文站名) 。
第n+2行到n+2+m行,为三个正整数x,y,z,代表x和y之间连一条权值为z的无向边。

输出格式

首先输出最短路长度:以及最短路径长度。
然后正序输出最短路径上站点的编号和站名信息。

因此可以将各各站点构造n个点,m条边的数据直接使用 DijkstraDijkstraDijkstra 算法即可求得最短路。
DijkstraDijkstraDijkstra 算法,经典的最短路算法,基于贪心思想的,适用于非负权值图的时间复杂度为Θ(n2)\Theta(n^2)Θ(n2)优秀算法。

优先队列优化思路:

由于DijkstraDijkstraDijkstra算法Θ(n2)\Theta(n^2)Θ(n2)的算法瓶颈是每次查找dis数组的最小的值,我们可以使用STL中的优先队列优化这一操作,将Θ(n)\Theta(n)Θ(n)的查找优化至O(logn)O(logn)O(logn)

但是我作为一名我校优秀的 ACMer 只是使用优先队列怎么行,我决定再进行一些优化(因为优先队列还是太慢啦),考虑如果要优化DijkstraDijkstraDijkstra算法,那么就要摒弃落后的优先队列,由于只需要借用优先队列寻找最小值操作,而优秀的线段树完全可以代替实现。因此我决定补充一个线段树优化版本的DijkstraDijkstraDijkstra。

线段树优化思路:

具体思路如下:DijkstraDijkstraDijkstra算法的核心思想就是每次取出最小的 dis[i],iϵ[1,n]dis[i],i\epsilon[1,n]dis[i],iϵ[1,n] 且当前的结点i并未被访问过。因此我们使用线段树来全程模拟这个操作。我们用线段树维护区间最小值。每次取出区间 [1,n][1,n][1,n]
中 disdisdis最小的点,作为当前节点。根据DijkstraDijkstraDijkstra的设计思想,当前节点的 disdisdis已经是最小的了,所以遍历该点能到达的所有子结点,更新子结点的 disdisdis,然后由于普通的线段树不支持删除操作(可持久化线段树支持,这里就不展开了)因此直接在线段树中把当前节点位置的值改成 +∞+∞+∞即可(模拟将该点删除)。

二者的理想时间复杂度虽然相同,都是Θ(nlogm)\Theta(nlogm)Θ(nlogm),但是线段树的常数更小,一般运行起来时间会是优先队列的12\frac{1}{2}21​!(至于我是怎么得到这个结论的,请看文末)

2. 输出最短路径思路分析:

我这里使用一个prev[j]prev[ j ]prev[j]来记录最短路上顶点 j 的前驱,可以在Θ(V)\Theta(V)Θ(V)的时间内完成最短路径的恢复(V是边数)。在d[j]d[ j ]d[j]被d[j]=d[k]+cost[k][j]d[ j ] = d[ k ] + cost[ k ][ j ]d[j]=d[k]+cost[k][j]更新时,修改prev[j]=kprev[ j ] = kprev[j]=k,这样就可以求出来prevprevprev的数组,可以在以上算法中用路径前驱标记法来还原出来路径。

为了输出站点名称,我使用56个int型整数代表56个沿途可能经过的站点。使用map<int,string>来存储站点信息(英文名)。

2)代码实现

1.数据

本次5条路线一共56个站点(结点)

通过百度百科查找成都市地铁信息梳理得到下面我所需要用到的站点列表:

地铁三号线站点信息
结点1~23

起点:石油大学站 Southwest Petroleum University
钟楼站 Clock Tower
马超西路站 Machao Road West
团结新区站 Tuanjiexinqu
锦水河站 Jinshuihe
三河场站 Sanhechang
金华寺东路站 Jinhua Temple Road East
植物园站 Botanical Garden
一期工程 军区总医院站 Chengdu Junqu General Hospital
熊猫大道站 Panda Avenue
动物园站 Chengdu Zoo
昭觉寺南路站 Zhaojuesi Road South
驷马桥站 Simaqiao
李家沱站 Lijiatuo
前锋路站 Qianfeng Road
红星桥站 Hongxing Bridge
市二医院站 Chengdu Second People’s Hospital
春熙路站 Chunxi Road
新南门站 Xinnanmen
磨子桥站 Moziqiao
省体育馆站 Sichuan Gymnasium
衣冠庙站 Yiguanmiao
高升桥站 Gaoshengqiao

地铁一号线站点信息:
结点:24~31

倪家桥站 Nijiaqiao
桐梓林站 Tongzilin
火车南站 South Railway Station
高新站 Hi-Tech Zone
金融城站 Financial City
孵化园站 Incubation Park
锦城广场站 Jincheng Plaza

终点:世纪城站 Century City

地铁一号线站点信息:
结点:32~37

人民北路站 North Renmin Road
文殊院站 Wenshu Monastery
骡马市站 Luomashi
天府广场站 Tianfu Square
锦江宾馆站 Jinjiang Hotel
华西坝站 Huaxiba

地铁二号线站点信息:
结点:38~39

牛王庙站 Niuwangmiao
东门大桥站 Dongmen Bridge

地铁六号线站点信息:
结点:40~46

顺江路站 Shunjiang Road
三官堂站 Sanguantang
东光站 Dongguang
琉璃场站 Liulichang
琉三路站 Liusan Road
金石路站 Jinshi Road
金融城东站 East Financial City

地铁九号线站点信息:
结点:47

心岛站 Xindao

地铁五号线站点信息:
结点:48~56

西北桥站Xibeiqiao
花牌坊站Huapaifang
抚琴站Fuqin
中医大省医院站Chengdu University of TCM &Sichuan Provincial People’s Hospital
青羊宫站Qingyang Taoist Temple
省骨科医院站Sichuan Provincial Orthopaedic Hospital
科园站Keyuan
九兴大道站Jiuxing Avenue
神仙树站Shenxianshu

梳理完56个结点有了这些信息以后就可以建图了!

然后手动造数据:
一共56个结点与结点信息(站名)和60条无向边

最后整张地铁图的数据及最后的测试数据如下:

输入格式

第一行两个正整数,n和m,表示图的结点数和边数。
第二行到n+1行,为n个字符串,代表n个站点信息(中间可能带空格的英文站名) 。
第n+2行到n+2+m行,为三个正整数x,y,z,代表x和y之间连一条权值为z的无向边。

输出格式

首先输出最短路长度:以及最短路径长度。
然后正序输出最短路径上站点的编号和站名信息。
样例:

56 60
Southwest Petroleum University
Clock Tower
Machao Road West
Tuanjiexinqu
Jinshuihe
Sanhechang
Jinhua Temple Road East
Botanical Garden
Chengdu Junqu General Hospital
Panda Avenue
Chengdu Zoo
Zhaojuesi Road South
Simaqiao
Lijiatuo
Qianfeng Road
Hongxing Bridge
Chengdu Second People's Hospital
Chunxi Road
Xinnanmen
Moziqiao
Sichuan Gymnasium
Yiguanmiao
Gaoshengqiao
Nijiaqiao
Tongzilin
South Railway Station
Hi-Tech Zone
Financial City
Incubation Park
Jincheng Plaza
Century City
North Renmin Road
Wenshu Monastery
Luomashi
Tianfu Square
Jinjiang Hotel
Huaxiba
Niuwangmiao
Dongmen Bridge
Shunjiang Road
Sanguantang
Dongguang
Liulichang
Liusan Road
Jinshi Road
East Financial City
Xindao
Xibeiqiao
Huapaifang
Fuqin
Chengdu University of TCM &Sichuan Provincial People’s Hospital
Qingyang Taoist Temple
Sichuan Provincial Orthopaedic Hospital
Keyuan
Jiuxing Avenue
Shenxianshu
1 2 1
2 3 1
3 4 1
4 5 1
5 6 1
6 7 1
7 8 1
8 9 1
9 10 1
10 11 1
11 12 1
12 13 1
13 14 1
14 15 1
15 16 1
16 17 1
17 18 1
18 19 1
19 20 1
20 21 1
15 32 1
18 35 1
32 33 1
33 34 1
34 35 1
35 36 1
36 37 1
37 21 1
32 48 1
48 49 1
49 50 1
50 51 1
51 52 1
52 53 1
53 23 1
23 22 1
22 21 1
23 54 1
54 55 1
55 56 1
56 26 1
21 24 1
24 25 1
25 26 1
26 27 1
27 28 1
28 29 1
29 30 1
30 31 1
18 39 1
39 38 1
38 40 1
40 41 1
41 42 1
42 43 1
43 44 1
44 45 1
45 46 1
46 47 1
47 29 1

2.优先队列优化版本程序

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
#include<map>
#define ls (p<<1)
#define rs (p<<1|1)
#define mid (l+r)/2
#define over(i,s,t) for(int i=s;i<=t;++i)
#define lver(i,t,s) for(int i=t;i>=s;--i)
using namespace std;const int N=1e3+7;
const int INF=1e9+7;
const int mod=2147483647;
const double EPS=1e-6;struct node
{int nex,v,val;
}edge[N<<2];int n,m;
int head[N],cnt,tot;
bool flag,vis[N];
int dis[N],pre[N],pa[N];
int st,ed;priority_queue< pair<int,int> >q;
map<int,string>mp;int read(){int x = 0, f = 1, ch = getchar();while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}while(isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar();return x * f;
}inline void add(int x,int y,int v)
{edge[++cnt].nex=head[x];edge[cnt].v=y;edge[cnt].val=v;head[x]=cnt;
}
void dijkstra()
{over(i,1,n)dis[i]=INF;memset(vis,false,sizeof vis);q.push(make_pair(0,st));dis[st]=0;while(!q.empty()){int u=q.top().second;q.pop();if(vis[u])continue;vis[u]=true;for(int i=head[u];i;i=edge[i].nex){int v=edge[i].v;if(dis[u]+edge[i].val<dis[v]){dis[v]=dis[u]+edge[i].val;q.push(make_pair(-dis[v],v));pre[v]=u;}}}
}
void print()
{for(int i=ed;i;i=pre[i])pa[++tot]=i;cout<<"最短路长度:"<<tot<<endl;cout<<"最短路径站点编号:"<<endl;lver(i,tot,2)cout<<pa[i]<<" -> ";cout<<pa[1]<<endl;cout<<"最短路径站点名称:"<<endl;lver(i,tot,2)cout<<mp[pa[i]]<<" -> ";cout<<mp[pa[1]]<<endl;return;
}
string str;
int main()
{st = 1;ed = 31;n = read(),m = read();for(int i = 1;i <= n;++i){getline(cin,str);mp[i] = str;}over(i,1,m){int x,y,z;x = read(),y = read(),z = read();add(x,y,z);add(y,x,z);}dijkstra();print();return 0;
}

3.线段树优化版本程序

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
#include<map>
#define over(i,s,t) for(int i=s;i<=t;++i)
#define lver(i,t,s) for(int i=t;i>=s;--i)using namespace std;
typedef long long ll;
const int N=1e3+7;
const int inf=1e9+7;
const int mod=2147483647;
const double EPS=1e-6;
map<int,string>mp;int read() {int x = 0, f = 1, ch = getchar();while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}while(isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar();return x * f;
}int n, m,tot;
int st,ed;
int pre[N],pa[N];struct edge {int to, w, nxt;edge() {}edge(int t, int ww, int nn) {to = t, w = ww, nxt = nn;}
}e[N << 1];int head[N], k = 0;
void add(int u, int v, int w) {e[k] = edge(v, w, head[u]); head[u] = k++;}ll ans[N];
struct node {ll dis; int x;node() {}node(ll d, int xx) {dis = d, x = xx;}
}dis[N << 2];void build(int p, int l, int r) {if(l == r) {dis[p].x = l; return;}int mid = l + r >> 1;build(p << 1, l, mid); build(p << 1 | 1, mid + 1, r);dis[p].x = dis[p << 1].x;
}void change(int p, int l, int r, int x, int y) {if(l == r) {dis[p].dis = y; return;}int mid = l + r >> 1;if(x <= mid) change(p << 1, l, mid, x, y);else change(p << 1 | 1, mid + 1, r, x, y);//单点修改的板子操作if(dis[p << 1].dis < dis[p << 1 | 1].dis) dis[p] = dis[p << 1];else dis[p] = dis[p << 1 | 1];
}node ask(int p, int l, int r, int ls, int rs) {if(ls <= l && r <= rs) {return dis[p];}int mid = l + r >> 1; node ans = node(inf, 0), tmp;if(ls <= mid) ans = ask(p << 1, l, mid, ls, rs);if(rs > mid) {node tmp = ask(p << 1 | 1, mid + 1, r, ls, rs);if(ans.dis > tmp.dis) ans = tmp;}return ans;
}void dijkstra() {for(int k = 1; k < n; k++) {register int u = ask(1, 1, n, 1, n).x;for(int i = head[u]; ~i; i = e[i].nxt) {register int v = e[i].to;if(ans[u] + e[i].w < ans[v]) {ans[v] = ans[u] + e[i].w;change(1, 1, n, v, ans[v]);pre[v]=u;}}change(1, 1, n, u, inf);}
}void print()
{for(int i=ed;i;i=pre[i])pa[++tot]=i;cout<<"最短路长度:"<<tot<<endl;cout<<"最短路径站点编号:"<<endl;lver(i,tot,2)cout<<pa[i]<<" -> ";cout<<pa[1]<<endl;cout<<"最短路径站点名称:"<<endl;lver(i,tot,2)cout<<mp[pa[i]]<<" -> ";cout<<mp[pa[1]]<<endl;return;
}string str;
int main() {st = 1;ed = 31;memset(head, -1, sizeof head);n = read(),m = read();for(int i = 1;i <= n;++i){getline(cin,str);mp[i] = str;}for(int u, v, w, i = 1; i <= m; i++)u = read(), v = read(), w = read(), add(u, v, w);for(int i = 1; i <= (n << 2); i++)dis[i].dis = inf;for(int i = 1; i <= n; i++)ans[i] = inf;//线段树初始化,dis是线段树,ans是答案build(1, 1, n);change(1, 1, n, st, 0); ans[st] = 0;dijkstra();print();return 0;
}

4.运行结果截图

两种代码的输出相同。因为我写的输出函数一摸一样啦,感兴趣的话您可以亲自运行一下

3)程序分析

最后到了算法分析的时间了!

算法分析:

时间复杂度

最基础的dijkstradijkstradijkstra算法的时间复杂度为Θ(n2)\Theta(n^2)Θ(n2)
而经过优化的dijkstradijkstradijkstra算法在核心代码部分,拥有while 循环和 for 循环,while 循环最多循环n 次(n 为顶点个数),for 循环一共循环每个点的边数,也就是总共循环m次。
而for 循环中往优先队列中添加删除数据的复杂度为Θ(logn)\Theta(log n)Θ(logn)。
线段树修改单点权值的时间复杂度也为Θ(logn)\Theta(log n)Θ(logn)

综合上述两部分,最终优化后的 DijkstraDijkstraDijkstra 算法的时间复杂度是Θ(mlogn)\Theta(mlogn)Θ(mlogn)
而使用普通线段树优化的Dijkstra算法的最差时间复杂度虽然也是Θ(mlogn)\Theta(mlogn)Θ(mlogn)
但是使用线段树的常数会小很多,虽然空间上大了两倍,但是在实际使用的时候时间可以压缩至使用优先队列的12\frac{1}{2}21​!

当然如果使用zkw线段树进行优化的话时间和空间可以进一步压缩至14\frac{1}{4}41​,那就是后话了。

空间复杂度

优先队列优化版本额外使用了dis数组和prev数组等等,空间复杂度为Θ(kn)=Θ(n)\Theta(kn) = \Theta(n)Θ(kn)=Θ(n)
线段树优化版本由于线段树的存在要开至少二倍空间,空间复杂度为Θ(2n+kn)=Θ(n)\Theta(2n+kn) = \Theta(n)Θ(2n+kn)=Θ(n)

下面给出luoguP4779 【模板】单源最短路径(标准版)三种方法运行的结果(本题中nnn和mmm的数据范围达到10510^5105,下图中的参数分别为运行时间/空间消耗/代码大小/语言),可以更清晰地看出三者的差别

优先队列优化:

线段树优化:

zkw线段树优化:

以上

【大作业】城市地铁线路最短路规划及路径输出(满分)相关推荐

  1. 【大作业】基于MATLAB的PRM算法的路径规划设计(随机地图+GUI+源码+报告)

    基于MATLAB的PRM算法的路径规划设计 下载链接: [Matlab期末大作业]基于MATLAB的PRM算法的路径规划设计(大报告+源代码+注释) 课题内容和要求 学会运用MATLAB 解决实际优化 ...

  2. python数据分析可视化大作业——对地铁数据的简单数据分析

    一.选题意义 随着我国经济的快速发展,我们国家的地铁事业正在快速发展,很多城市都拥有了地铁.自1969年北京开通第一条地铁线路建成通车,到2021年全国总线路总长达7253.73公里,我们只用了40年 ...

  3. HTML5+CSS3期末大作业——城市简介

    页面展示: 首页源码: <!DOCTYPE html> <html lang="en"> <head><meta charset=&quo ...

  4. 183条地铁线路,3034个地铁站,发现中国地铁名字的秘密。

    作者 | 小F 来源 | 法纳斯特 最近看了新周刊的一篇推送,有关地铁名字的分析,链接如下. 我们分析了3447个地铁站,发现了中国城市地名的秘密 于是乎也想着自己去获取数据,然后进行分析一番. 当然 ...

  5. 课程项目:大学程序设计相关大作业汇总参考及源码地址

    C++程序设计 利用C++实现的小游戏:2048,俄罗斯方块和贪吃蛇[Some small games implemented in C++: 2048, Tetris and Snake (Univ ...

  6. DS博客大作业--树

    1.树的存储结构说明 树节点结构体 data:文件名 brother:兄弟节点 child:孩子节点 type:节点的类型,0为文件,1为目录 h:节点所在的层次 2.树的函数说明 头文件 函数1:C ...

  7. DHU Python Curriculumly Learning【1】——大作业_own work

    文章目录 写在前面 第一次大作业 第二次大作业 第三次大作业 第四次大作业 第五次大作业 第六次大作业 第七次大作业 第八次大作业 第九次大作业 第十次大作业 写在前面 本博客用于记录(或者说是用来备 ...

  8. HTML期末大作业 ~ 马尔代夫旅游网页设计作业成品 ~ HTML+CSS+JS网页设计期末课程大作业 ~ web前端开发技术 ~ web课程设计网页规划与设计...

    HTML期末大作业 ~ 大学生旅游官网网页设计作业成品 ~ HTML+CSS+JS网页设计期末课程大作业 ~ web前端开发技术 ~ web课程设计网页规划与设计~ 临近期末, 你还在为HTML网页设 ...

  9. 【LibreOJ】#6395. 「THUPC2018」城市地铁规划 / City 背包DP+Prufer序

    [题目]#6395. 「THUPC2018」城市地铁规划 / City [题意]给定n个点要求构造一棵树,每个点的价值是一个关于点度的k次多项式,系数均为给定的\(a_0,...a_k\),求最大价值 ...

最新文章

  1. Oracle存储过程返回游标实例详解
  2. 开机后需要手动打开mysql_mysql解压版一键配置
  3. 控制器中添加DB类才可以操作数据库表中的数据
  4. Batch Normalization导读
  5. 教程-Spark安装与环境配置
  6. flowable 设置流程跟踪高亮线的颜色
  7. mysql5.7空间运算,深度解析MySQL5.7之临时表空间
  8. flog和flag_FLAG:写作,英语和持续学习
  9. java 多线程并容器实现_跟着实例学习java多线程9-并发容器
  10. json标注工具与labelme安装
  11. 这打车App麻烦了!遭黑客勒索巨额比特币
  12. 世界500强:公司面试:智力题集锦(附答案)
  13. idea 内嵌浏览器 翻译
  14. strcmp()函数用法及其详解
  15. java间接调用_无法解析类型 java.util.Map$Entry。从必需的 .class 文件间接引用了它...
  16. openCV实践项目:银行卡卡号识别
  17. 阿里巴巴Mysql规范
  18. 出生年分数 15作者 陈越单位 浙江大学
  19. 在若依项目中添加导入excel功能
  20. 解决QQ群文件未通过安全检测无法下载问题

热门文章

  1. 轻松学Pytorch – 年龄与性别预测
  2. U平方Net:深入使用嵌套的U型结构进行显著目标检测
  3. 从深度图到点云的构建方式
  4. 青春就是一无所有,梦想就是坚持走下去
  5. 揭秘:深度网络背后的数学奥秘
  6. 进程间通信的几种方式
  7. 设计模式-建造者模式(05)
  8. UIWebView滚动监听
  9. iPhone浏览器性能测试
  10. 新概念机房运维监控三大新理念