学校“数据结构”课程Project—扩展功能(自主设计)

目录

一、设想功能描述

想法缘起

目标功能

二、问题抽象

三、算法设计和优化

1. 易想的朴素搜索 / dp

搜索想法

动态规划(dp)想法

2. 思考与优化

四、算法实现

五、结果示例

附:使用的地图API


一、设想功能描述

想法缘起

  • OSM 导出的地图数据中有 “amenity”(便民设施)点,并在 <tag k="amenity" v="..."/> 中标记了每个 amenity 的具体类型,比如:fast_food, pub, toilets……

  • 而人们在路途或旅途中,常常关注最近的某种类型(常为 amenity,如卫生间、停车车场)的最近处,而尚未明确目的地。从而容易想到去设计一个扩展功能:给定 amenity,查询距离当前点最近的该类的点显然,这个问题仍用 Dijkstra 求单源最短路即可。

  • 然而,有时人们想连续去多个便民点,比如:先去停车场停车再去吃饭,最后逛逛周边的公园等等。为了提高效率与体验,很可能需要一个总路程最短的方案,这就是该 “扩展功能” 考虑的问题。

目标功能

★ 用户给定当前位置,并指定一个 amenity 种类的顺序。求出最短路线,并呈现在地图上。

二、问题抽象

地图上有某些点带有颜色。给出起点 s 与颜色序列 [c_1,c_2,\dots,c_n]尽可能快地求出一条以 s 为起点的最短路线 P,要求其依次经过颜色为 c_1,c_2,\dots,c_n 的点。

存在数据约束与特征:|C_i|\le 400,其中 C_ii 颜色点集,此外,各种颜色的点大致均匀分布。

三、算法设计和优化

1. 易想的朴素搜索 / dp

搜索想法

直接以每个 c_i 为阶段,进行深度优先搜索(DFS),这部分时间复杂度已经有 O(\Pi_{i=1}^n|C_i|),虽然可以进行剪枝,即当前搜素的长度 cur 超过搜到的最短长度 res,就直接 return,但光这样时间复杂度不变的。

动态规划(dp)想法

以每个 c_i 为阶段设计 dp,易得状态转移方程类似如下形式:

f[i][u]=\max_{v\in C_{i-1}}\{f[i-1][v]+dis(v,u)\},u\in C_i

然而,显然其时间复杂度是同搜索的,而且还很难剪枝,所以比搜索更差。

此外,还要计算中间相邻颜色两点的最短路,从而全求出来的时间复杂度应比上面更大,空间复杂度亦大。

2. 思考与优化

直观地,最终答案的路线几乎不可能 “兜得很远”,这启发我们优化搜索顺序。对于每个 u\in C_i,我们考虑先走到离最近的 v_1\in C_{i+1} 搜索下去,回溯后再搜次近的 v_2\in C_{i+1}……可以想象这样搜出来的前几条路已经得到或较接近最终答案。进一步来看,又因为各种颜色的点大致均匀分布,所以这样的剪枝效果一定是非常显著的。

不过,如何求出搜 v\in C 的顺序?其实我们完全不必先预处理出最短路然后排序。注意到 Dijkstra 的最短路算法就是按照距离由到达的顺序得到 “确定点” 的。所以,我们把 Dijkstra 算法 “嵌入” 搜索框架之中,从当前 u\in C_i 做 Dijkstra,遍历到一个下一颜色的 v\in C_{i+1},我们就从 v 递归进行搜索。这样相当于又解决了花大代价计算最短路的问题。

总之,本问题基于搜索的大框架,而其内部融入 Dijkstra,每个结点处的下一个点通过 Dijkstra 确定,当前搜索路上的每个点都保留了一个 Dijkstra 状态。

四、算法实现

关键在于构建搜索的代码,尤其是解决 “每一层” 都在做 Dijkstra 带来的问题

原始 Dijkstra 只有一个答案数组 d[MAXN] ,这里我们显然需要多个,不过我们若开 n 个这样的数组,内存开销较大,且可能不易扩展。注意到 {d[u], u} 保留在原始 Dijkstra 的 std::priority_queue 中,我们可以std::set 代替这个 std::priority_queue,既可以取出最小值,又存下来当前有用的 {d[u], u} 且能 std::find 得到 {d[v], v},而 vis 数组可用 std::unordered_set 代替

这样,我们相当于仅在搜索路上开 C++ 的容器,空间开销显著减小而并未增大过多增加时间常数,且易于扩展。

void DFS(int s, int x, double cur) {if (x == Ord.size()) { // ... }set<pair<double, int>> d; d.insert({0.0, s});unordered_set<int> vis;while (d.size()) {int u = d.begin()->second;double dis = d.begin()->first;if (cur + dis > ans) return;
​d.erase(d.begin());if (vis.find(u) != vis.end()) continue;vis.insert(u);auto itt = M.p[u].Ames.find(Ord[x]);if (itt != M.p[u].Ames.end()) {curOrd.push_back(u);DFS(u, x+1, cur+dis);curOrd.pop_back();}
​for (int e = M.head[u]; e; e = M.nxt[e]) {// ...auto it = d.upper_bound({-1.0, v});if (it==d.end() || it->second!=v || dis+w<it->first) d.insert({dis+w, v});}}
}

五、结果示例

  • n=6:起点在徐家汇一带,(较为随意地)指定下面 6 个 Amenity Type 的顺序:

附:使用的地图API

        基于 Leaflet.js 库的 Python 交互式地图包 Folium

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/434008.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【JavaEE进阶】 #{}和${}

文章目录 &#x1f343;前言&#x1f333;#{}和${}使⽤&#x1f6a9;Interger类型的参数&#xff08;基础数据类型&#xff09;&#x1f388;使用#{}&#x1f388;使用${} &#x1f6a9;String类型的参数使用&#x1f388;#{}使用&#x1f388;${} &#x1f38d;#{}和${}区别&a…

数据库之 基础概念、安装mysql、sql语句基础

数据库之 基础概念、安装mysql、sql语句基础 【一】存储数据的演变过程&#xff1a; 文件存储&#xff1a; 初始阶段随意存放数据到文件&#xff0c;格式任意。目录规范引入&#xff1a; 软件开发使用目录规范&#xff0c;限制数据位置&#xff0c;建立专门文件夹。本地数据存…

java版代码生成器

之前实现的JRT代码生成器是M版的&#xff0c;那么用户必须用M库才能有代码生成器的功能。为了提供给就是不用M库的用户使用&#xff0c;JRT再提供脚本版的java代码生成器&#xff0c;方便直接连关系库生成JRT的代码。 实现&#xff1a; import JRT.Core.MultiPlatform.JRTCon…

接口测试怎么测?接口测试的流程和步骤(超详细)

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;薪资嘎嘎涨 一、什么是接口测试 我们要想知道接口测试怎么做&#xff0c;首先要明白接口测试是什么?一般…

<蓝桥杯软件赛>零基础备赛20周--第18周--动态规划初步

报名明年4月蓝桥杯软件赛的同学们&#xff0c;如果你是大一零基础&#xff0c;目前懵懂中&#xff0c;不知该怎么办&#xff0c;可以看看本博客系列&#xff1a;备赛20周合集 20周的完整安排请点击&#xff1a;20周计划 每周发1个博客&#xff0c;共20周。 在QQ群上交流答疑&am…

HCS-华为云Stack-FusionSphere

HCS-华为云Stack-FusionSphere FusionSphere是华为面向多行业客户推出的云操作系统解决方案。 FusionSphere基于开放的OpenStack架构&#xff0c;并针对企业云计算数据中心场景进行设计和优化&#xff0c;提供了强大的虚拟化功能和资源池管理能力、丰富的云基础服务组件和工具…

栈和队列的动态实现(C语言实现)

✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅ ✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨ &#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1…

【LeetCode】112. 路径总和(简单)——代码随想录算法训练营Day18

题目链接&#xff1a;112. 路径总和 题目描述 给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径&#xff0c;这条路径上所有节点值相加等于目标和 targetSum 。如果存在&#xff0c;返回 true &#xff1b;否则&…

ENVI下基于知识决策树提取地表覆盖信息

基于知识的决策树分类是基于遥感影像数据及其他空间数据,通过专家经验总结、简单的数学统计和归纳方法等,获得分类规则并进行遥感分类。分类规则易于理解,分类过程也符合人的认知过程,最大的特点是利用的多源数据。 决策树分类主要的工作是获取规则,本文介绍使用CART算法…

第17章_反射机制(理解Class类并获取Class实例,类的加载与ClassLoader的理解,反射的基本应用,读取注解信息,体会反射的动态性)

文章目录 第17章_反射机制本章专题与脉络1. 反射(Reflection)的概念1.1 反射的出现背景1.2 反射概述1.3 Java反射机制研究及应用1.4 反射相关的主要API1.5 反射的优缺点 2. 理解Class类并获取Class实例2.1 理解Class2.1.1 理论上2.1.2 内存结构上 2.2 获取Class类的实例(四种方…

每日一题——LeetCode1346.检查整数及其两倍数是否存在

方法一 循环查找 用indexOf查找每个元素的两倍是否存在在数组中&#xff0c;找到了就直接return true&#xff0c;循环结束还没找到就return false var checkIfExist function(arr) {for(let i0;i<arr.length;i){let index arr.indexOf(arr[i]*2)if(index>0 &&…

小土堆pytorch学习笔记003 | 下载数据集dataset 及报错处理

目录 1、下载数据集 2、展示数据集里面的内容 3、DataLoader 的使用 例子&#xff1a; 结果展示&#xff1a; 1、下载数据集 # 数据集import torchvisiontrain_set torchvision.datasets.CIFAR10(root"./test10_dataset", trainTrue, downloadTrue) test_set …