由于疫情原因在家混吃等死的SDU计科学子,迎来了又一个需要熬夜肝的课程–数据结构课程设计。尽管前期的竞赛树、二叉树森林转换、高性能跳表写的我痛不欲生,但是不可否认的是,数据结构这门课的确如很多大佬所说,是一门有无限可能的课程,利用好数据结构进行开发绝非学好表面知识这么简单且快速的过程。要想将所有数据结构烂熟于心,使用时如应激反应一样的快速且精准,还有很长的路要走。
好了下面进入正题,由于本文与Dijkstra算法高度相关,建议还不了解或者忘记了Dijkstra细节的同学移步这边传送门,了解了Dij基本使用场景和算法细节之后继续阅读。
题目简述
最短路径问题是图论中的一个经典问题,其中的Dijkstra算法一直被认为是图论中的好 算法,但有的时候需要适当的调整Dijkstra算法才能完成多种不同的优化路径的查询。
对于某城市的公交线路,乘坐公交的顾客希望在这样的线路上实现各种优化路径的查询。 设该城市的公交线路的输入格式为: 线路编号:起始站名(该站坐标);经过的站点1名(该站坐标);经过的站点2名(该站坐 标);……;经过的站点n名(该站坐标);终点站名(该站坐标)。该线路的乘坐价钱。该线路平 均经过多少时间来一辆。车速。
例如:63:A(32,45);B(76,45);C(76,90);……;N(100,100)。1元。5分钟。1/每分钟。 假 定线路的乘坐价钱与乘坐站数无关,假定不考虑公交线路在路上的交通堵塞。对这样的公交 线路,需要在其上进行的优化路径查询包括:任何两个站点之间最便宜的路径;任何两个站 点之间最省时间的路径等等。
基本要求
① 根据上述公交线路的输入格式,定义并建立合适的图模型。
② 针对上述公交线路,能查询获得任何两个站点之间最便宜的路径,即输入站名S, T后, 可以输出从S到T的最便宜的路径,输出格式为:线路x:站名S,…,站名M1;换乘线路x: 站名M1,…,站名M2;…;换乘线路x:站名MK,…,站名T。共花费x元。
③ 针对上述公交线路,能查询获得任何两个站点之间最省时间的路径(不考虑在中间站 等下一辆线路的等待时间),即输入站名S,T后,可以输出从S到T的考虑在中间站等下一 辆线路的等待时间的最省时间的路径,输出格式为:线路x:站名S,…,站名M1;换乘线 路x:站名M1,…,站名M2;…;换乘线路x:站名MK,…,站名T。共花费x时间。
④ 针对上述公交线路,能查询获得任何两个站点之间最省时间的路径(要考虑在中间站 等下一辆线路的等待时间),即输入站名S,T后,可以输出从S到T的考虑在中间站等下一 辆线路的等待时间的最省时间的路径,输出格式为:线路x:站名S,…,站名M1;换乘线 路x:站名M1,…,站名M2;…;换乘线路x:站名MK,…,站名T。共花费x时间。 实现提示
题目是瞎眼可见的图论算法,看完第一遍你可能会觉得:哎好简单这个题!
如果作为一道程序设计ACM方向的题,暴力Dij直接实现可能容易的很,但是考虑到与数据结构这门课的联系性,不可绕开的两个话题便是:封装性和复用性。我们不妨先将完善这两个特点作为前提,重新审视整道题目:
题目的场景是一个二维图,每个点都用x,y两个坐标,现给出n趟公交线路,每趟公交线路设计几个因素:站点(二维坐标点)、票价、车速、等待时间。其中站点信息和车速共同标识了一个隐含信息—路途时间。
根据题目的要求和常识,站点的坐标、票价、等待时间、车速都为整数型,考虑到数值的大小,使用int存放即可。但根据距离的计算公式
sqrt[(x1-x2)2+(y1-y2)2]
利用数学上的两点间计算公式计算出的实际距离很有可能是小数,为了方便后续的操作,我们将double型的距离与int型的速度值做除法后,结果得到int型的时间(向上取整)。虽然该算法计算出的结果和真实值略有差距但是该误差不影响后续算法。
题目给出的问题有三个,但是都与最短路算法息息相关:
1、在不考虑等车基础上(换乘立刻完成),计算最短时间和路线
2、找到一条花费车票费用最少的路线
3、在考虑等车时间的基础上,计算最短时间的路线
对上述三个问题的题面和要求分析,我们发现问题求解的几个要点:
1、求解的路线是两个坐标之间的路线,也就是单源最短路,确定了Dij的可行性
2、要求在输出最短路线的同时,输出路径上经过的各个车站和换乘路线。这一条是非常重要的,了解了这个要求在设计算法是就要注意路径的回溯问题和换乘线路的记录。
3、根据实际情况,政府一般不会将多条重复路径安排在不同的线路上,因此在设计算法是可以假设不同的线路其站点路径无重复。如果使用者在使用者需要使用带有重复路径的数据集,可以修改较小的数据存储保证可行性。
根据上述的问题分析,我们可以将整个问题分为两个部分:
1、路线图的存储
2、在路线图上设计各种Dij及其变形算法
整条的路线一般是不容易进行存放的,但是我们可以将整条的路线拆解成多段路径,在每一段路径进行存储时,使用线路id来做以区分。
利用这种线路拆解的思路,将存放一整条路线的问题转换成了存放散边和一些关于边的信息的问题。这种存边的实现是在图论部分中多次使用的邻接表。
邻接表实现
邻接表两种实现原理相似,主要差别在数据的存储组织方式上:
数组&&vector
实现方式:使用vector存放每个点出发的边信息,使用vector指针来管理全部vector从而实现数据的存储和查找。
优点:实现容易,查找、添加操作均可调用C++的vector实例直接使用即可。且由于使用了vector的存储方式,数据在存储时多为连续空间存放,连续访问某个节点的数据时,vector内存池能够提供很好的查找性能。
缺点:由于所有操作都需要通过vector调用类方法,时间性能较差,在处理大量数据时容易造成比较大的时间损失。
链表
实现方式:使用头结点指针来管理每个点起始的前置节点指针,通过前置节点指针来访问全部数据。
优点:所有使用的存储空间都通过动态申请的方式获得,且所有节点的访问都使用指针直接访问,运行速度快。
经过上述的两种分析,我们已经完成了图存储的框架,但是由于该图的点是二维点,就意味着上述的每种存放方式都要经过一定的处理保证存放正确:
vector存放方式:注意管理vector的指针为一个二维指针,通过(x,y)–>vector[i][j],映射到相应的vector中。
链表实现方式:进行前置节点管理的指针为二维指针,通过(x,y)–>p[i][j],映射到相应的前置节点指针地址。
存放线路时间
完成了上面的ADT设计,关于图的基本计算已经可以实现,但是由于需求三要计算带有等待时间的最短时间线路。考虑到题目所给的条件只有发车的间隔,且直接使用发车间隔作为换成的等待时间并不合理。一种更加合理的算法是:计算出所有线路从两个端点站发车到每个车站所需要的时间,结合发车的间隔,利用这两个量就可以完成第三问的要求,具体的实现细节在问题实现部分展开。
考虑到每个站点可能有多条线路汇聚,且每条线路都是双向行驶的,设计的数据结构要能够保证区分开线路的同时,区分开线路的行驶方向。
经过和队友的讨论设计,我们采取了如下的存储方案:
对地图上的每个坐标点(x,y),使用map存储其有关形式时间的信息:
map<int, pair<int, int>> record;
map的key项是线路id,每一个线路id都能够唯一标识一个pair
int direct;
使用direct来标识每条边的正向和逆向,方便计算使用,规定按照输入顺序行驶的方向即为正向线路,反之即为逆向线路。
同时为了尽可能的提高后续查发车间隔的时间性能,采用map来存放不同id对应的发车间隔。考虑到map底层的红黑树结构,查效率可以优化至O(logn)。(这里还可以进一步优化,因为数据的非顺序性,可以使用hashmap,将查效率进一步优化到O(1),哈希大法好!)
map<int, int> wait_map;//存放每趟路线的发车间隔
完成了上述的设计后,数据的初始化已经可以完成,添加路线的封装函数设计如下:
void add_line(vector<pair<int, int>>* station, int speed, int value,int wait_time,int id)
{
route_num++;
wait_map[id] = wait_time;
for (int i = 0; i < (station->size() - 1); i++)
{
int x1 = station->at(i).first;
int y1 = station->at(i).second;
int x2 = station->at(i + 1).first;
int y2 = station->at(i + 1).second;
edge * plist = graph_p[x1].vertex[y1].first_edge;//指向头结点
edge* new_node = new edge();
new_node->initialize(x1, y1, x2, y2, id, value, speed,0);//车正向行驶的边标号为0
new_node->next = plist;
graph_p[x1].vertex[y1].first_edge = new_node;
}
for (int i = station->size() - 1; i >= 1; i--)
{
int x1 = station->at(i).first;
int y1 = station->at(i).second;
int x2 = station->at(i - 1).first;
int y2 = station->at(i - 1).second;
edge * plist = graph_p[x1].vertex[y1].first_edge;//指向头结点
edge* new_node = new edge();
new_node->initialize(x1, y1, x2, y2, id, value, speed,1);//车逆向行驶的边标号为1
new_node->next = plist;
graph_p[x1].vertex[y1].first_edge = new_node;
}
int start_x = station->at(0).first;
int start_y = station->at(0).second;
int end_x = station->at(station->size() - 1).first;
int end_y = station->at(station->size() - 1).second;
double a = (start_x - end_x) * (start_x - end_x) + (start_y - end_y) * (start_y - end_y);
int route_dis = (int)sqrt(a);
int route_time = (route_dis / speed) + 1;
point_map[start_x][start_y].insert_time(id, 0, route_time);
point_map[end_x][end_y].insert_time(id, route_time, 0);
int route_time_plus = 0;
int route_time_minus = 0;
for (int i = 1; i < station->size() - 1; i++)
{
int dx = station->at(i).first;
int dy = station->at(i).second;
a = (dx - start_x) * (dx - start_x) + (dy - start_y) * (dy - start_y);
route_dis = (int)sqrt(a);
route_time_plus = (route_dis / speed) + 1;
a = (dx - end_x)*(dx - end_x) + (dy - end_y)*(dy - end_y);
route_dis = (int)sqrt(a);
route_time_minus = (route_dis / speed) + 1;
point_map[dx][dy].insert_time(id, route_time_plus, route_time_minus);
}
}
链表实现路线图ADT源码:
struct edge
{
pair<int, int> begin_point;//起点
pair<int, int> des_point;//终点
int id;
int time;
int cost;
int direct;
edge* next;//指向下一个
}
struct point_list
{
map<int, pair<int, int>> record;
};
struct y_list//标识每个定点y坐标的结构体
{
edge* first_edge;
};
struct x_list
{
y_list* vertex;
};
class Graph
{
private:
int vertex_num;//出发点的个数
int route_num;//路径的条数
x_list* graph_p;//存图
map<int, int> wait_map;//存放每趟路线的发车间隔
point_list** point_map;//存放每个点的到每一个始发站和终点站的时间
}
完成了上述的ADT设计,数据的存储和初始化都可以顺理成章的完成了,下面需要针对具体的问题进行具体的分析并给出问题的具体求解思路。Dij算法的核心在于松弛算法的松弛原则和具体松弛操作的设计,本部分将围绕这两个主题进行。
需求1的要求是实现不考虑等待时间的最短时间路线设计。显而易见的是,由于求解的是最短时间问题,每个点不可能被重复经过,否则两次重复经过之间的时间是浪费掉的。
由于没有其他因素影响结果,可以将路线行驶花费时间直接作为Dij的参照因素,使用基本的Dij算法实现即可。但是因为要输出行驶线路的站点和换乘路线,需要在跑Dij算法的同时,维护两个数据结构存储上述两种信息。
int** route_id = NULL;//路线id的前置矩阵
pair<int, int>** front_point = NULL;//前置节点的访问矩阵
front_point记录了每个点的前置节点,把起始点的前置节点置为空即可。在输出路径是,从终点回溯并将各点加入vector,完成后倒序输出即可。
route_id记录了到每个点所经过的路线id,其中起始点的id=0,在输出路径id时,比对前后两条线路的id,如果相同说明仍然在该线路上行驶,如果不相同说明换乘了其他线路,输出换乘的id号即可。
具体的算法流程如下:
1、初始化数据结构
2、维护一个小根堆,小根堆的key是到源点需要的时间。将源点加入小根堆
3、取出小根堆的头结点,遍历其邻接边,并做松弛操作。
松弛原则:
如果源点行驶到该边起点时间+该边行驶时间<源点行驶到该边终点时间,进行松弛。
松弛操作:
1、将时间数组更新
2、将终点加入小根堆
3、更新终点的前置节点数组
4、更新终点的前置路线id数组
5、更新当前行驶的id号
if (dis[des_x][des_y] > dis[dx][dy] + p->time)
{
dis[des_x][des_y] = dis[dx][dy] + p->time;
front_point[des_x][des_y].first = dx;
front_point[des_x][des_y].second = dy;
route_id[des_x][des_y] = p->id;
q.push(make_pair(-dis[des_x][des_y], make_pair(des_x, des_y)));
}
需求2求解的问题是最少车票花费问题,车票的花费主要受两个因素的影响:
1、单张车票的价格
2、换乘次数,换乘时要花费新的车票费用。
因为求解的问题是最少花费问题,可以显而易见的是,当某乘客乘坐i号线路并换乘到j号线路后,她不会再上i号线路,乘坐过程中也不会重复经过某个点,因为这两种决策都会导致时间的浪费。因此可以规定Dij过程中维持的原则:每个点不被重复经过且每个线路只能走一次。
关于算法的流程与上述相同,这里不再赘述,值得展开的是松弛
松弛原则:
如果源点到该边起点花费+该边花费<源点到该边终点花费,进行松弛
但是与需求1不同的是·,这里的边花费不再是可以直接访问边信息得到,而是需要进行一定的处理:
首先在记性Dij的过程中,定义一个变量存储当前走的线路:
int last_id = route_id[dx][dy];
在进行松弛之前,访问到边的id与当前线路id做比较:
如果相同说明更新的线路和正在乘坐的线路id相同,不需要花费票价就可以继续乘坐。
如果不相同说明松弛这个点需要进行换乘,要花费新的票价乘坐。
利用这样的数据处理,就可以将较复杂的花费问题转换成基本的Dij操作,从而轻松解决。
松弛操作:
1、将花费数组更新
2、将终点加入小根堆
3、更新终点的前置节点数组
4、更新终点的前置路线id数组
if (last_id == p->id)
new_price = 0;
else
new_price = p->cost;
if (price[des_x][des_y] > price[dx][dy] + new_price)
{
price[des_x][des_y] = price[dx][dy] + new_price;
front_point[des_x][des_y].first = dx;
front_point[des_x][des_y].second = dy;
route_id[des_x][des_y] = p->id;
q.push(make_pair(-price[des_x][des_y], make_pair(des_x, des_y)));
}
需求3在需求1的基础上,考虑等车时间,并选出最省时的路径。
虽然题目给定了发车间隔,但是在使用者使用该程序时,默认整个公交系统从起始态开始运行,每辆车需要不同的时间到每个站点,因此在求解的同时,我们需要模拟公交系统的形式状态,这样才能给出真实的等待时间。
分析公交的运行方式,可以总结使用者在某站等车时有两种情况:
1、第一班车还没到,需要等待时间=始发站到该站的时间—使用者到该站的时间
2、第一版车已经过去,需要等待的时间=(使用者到该站的时间–始发站到该站的时间)%线路发车间隔
上述计算时间所用到的变量有三个始发站到该站的时间、使用者到该站的时间、线路发车间隔,其中线路发车间隔在设计ADT中我们将其存放在了map中可以随时查找;使用者到该站的时间作为该算法的松弛指标dis[x][y];需要特殊处理的是始发站到该站的时间。
对于该变量,我和队友在设计时有两种设计思想:
1、不存储始发站到该站的时间,而是利用使用者到该站的时间,每次使用时直接动态计算出需要等待的时间
2、在初始化线路时,将始发站到该站的时间提前计算好并且存放下来,在后续使用时直接查找并套入上述公式计算等待的时间。
两种算法代表了两种思想:
算法1使用时间换空间,不需要为线路的运行时间分配空间,但由于每次调用需要计算,存在大量的重复计算,时间性能差但是占用空间小;
算法2使用空间换取时间,存储下线路各站点的运行时间,在每次松弛操作前套用公式即可,时间性能优秀但占用空间。
经过线路占用空间比较和大数据集对动态计算的影响,进行权衡过后,我们得出了这样的结论:存储站点行驶时间占用的空间远小于存放线路图占用的空间,空间耗费几乎可以忽略不计。但是在大数据集下,算法2对比算法1拥有明显的时间性能优势。因此采取算法2,使用存储信息–>套公式的方法来计算得到需要等待的时间。
松弛原则:
这里要注意的是,上述的算法描述都基于换乘等待时间。需求3的实现基于需求1和需求2做了结合,在考虑行驶时间的同时,也要考虑线路问题,如果待更新边的线路号与当前行驶线路号相同,等待时间为0;只有在线路号不同要进行换乘时,需要使用上述的算法进行求解。
可以将松弛的准备操作描述如下:
1、判断待更新边的路线id与当前行驶id的关系,如果相等则等待时间为零,直接向下完成松弛
2、如果不相等,需要换乘操作,访问存储查找数据并套用公式,计算出等待时间。
如果源点到该边起点时间+该边行驶时间+换乘等待时间<源点到该边终点时间,进行松弛
松弛操作:
1、将时间数组更新
2、将终点加入小根堆
3、更新终点的前置节点数组
4、更新终点的前置路线id数组
5、更新当前行驶的id号
if (des_direct == 0)//边是正向
{
{
need_time = point_map[dx][dy].record[des_id].first;
}
else if (des_direct == 1)//边是负向
{
need_time = point_map[dx][dy].record[des_id].second;
}
if (dis[dx][dy] >= need_time)//到这个点的时候,第一班车已经走了
{
wait_time = (dis[dx][dy] - need_time) % wait_map[des_id];
}
else//到这个点的时候,车还没到
{
wait_time = need_time - dis[dx][dy];
}
}
if (dis[des_x][des_y] > dis[dx][dy] + des_time + wait_time)
{
dis[des_x][des_y] = dis[dx][dy] + des_time + wait_time;
front_point[des_x][des_y].first = dx;
front_point[des_x][des_y].second = dy;
route_id[des_x][des_y] = p->id;
q.push(make_pair(-dis[des_x][des_y], make_pair(des_x, des_y)));
}
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int max_i = 1e8;
struct edge
{
pair<int, int> begin_point;//起点
pair<int, int> des_point;//终点
int id;
int time;
int cost;
int direct;
edge* next;//指向下一个
edge()
{
this->begin_point.first = 0;
this->begin_point.second = 0;
this->des_point.first = 0;
this->des_point.second = 0;
this->id = 0;
this->time = 0;
this->cost=0;
this->direct = -1;
this->next = NULL;
}
void initialize(int begin_x, int begin_y, int des_x, int des_y, int id, int value, int speed,int dir)
{
begin_point.first = begin_x;
begin_point.second = begin_y;
des_point.first = des_x;
des_point.second = des_y;
double a = (begin_x - des_x) * (begin_x - des_x) + (begin_y - des_y) * (begin_y - des_y);
int route = (int)sqrt(a);
int route_time = (route / speed) + 1;
this->id = id;
this->time = route_time;
this->cost = value;
this->direct = dir;
}
};
struct point_list
{
map<int, pair<int, int>> record;
point_list()
{
record.clear();
}
void insert_time(int id, int plus_time, int minus_time)
{
record[id] = make_pair(plus_time, minus_time);
}
};
struct y_list//标识每个定点y坐标的结构体
{
edge* first_edge;
};
struct x_list
{
y_list* vertex;
};
class Graph
{
private:
int vertex_num;//出发点的个数
int route_num;//路径的条数
x_list* graph_p;//存图
map<int, int> wait_map;//存放每趟路线的发车间隔
point_list** point_map;//存放每个点的到每一个始发站和终点站的时间
public:
Graph(int graph_num)
{
vertex_num = graph_num;
route_num = 0;
graph_p = new x_list[vertex_num + 1];
point_map = new point_list * [vertex_num + 1];
for (int i = 0; i <= vertex_num; i++)
{
graph_p[i].vertex = new y_list[vertex_num + 1];
point_map[i] = new point_list[vertex_num + 1];
for (int j = 0; j <= vertex_num; j++)
{
graph_p[i].vertex[j].first_edge = NULL;
}
}
wait_map.clear();
}
void add_line(vector<pair<int, int>>* station, int speed, int value,int wait_time,int id)
{
route_num++;
wait_map[id] = wait_time;
for (int i = 0; i < (station->size() - 1); i++)
{
int x1 = station->at(i).first;
int y1 = station->at(i).second;
int x2 = station->at(i + 1).first;
int y2 = station->at(i + 1).second;
edge * plist = graph_p[x1].vertex[y1].first_edge;//指向头结点
edge* new_node = new edge();
new_node->initialize(x1, y1, x2, y2, id, value, speed,0);//车正向行驶的边标号为0
new_node->next = plist;
graph_p[x1].vertex[y1].first_edge = new_node;
}
for (int i = station->size() - 1; i >= 1; i--)
{
int x1 = station->at(i).first;
int y1 = station->at(i).second;
int x2 = station->at(i - 1).first;
int y2 = station->at(i - 1).second;
edge * plist = graph_p[x1].vertex[y1].first_edge;//指向头结点
edge* new_node = new edge();
new_node->initialize(x1, y1, x2, y2, id, value, speed,1);//车逆向行驶的边标号为1
new_node->next = plist;
graph_p[x1].vertex[y1].first_edge = new_node;
}
int start_x = station->at(0).first;
int start_y = station->at(0).second;
int end_x = station->at(station->size() - 1).first;
int end_y = station->at(station->size() - 1).second;
double a = (start_x - end_x) * (start_x - end_x) + (start_y - end_y) * (start_y - end_y);
int route_dis = (int)sqrt(a);
int route_time = (route_dis / speed) + 1;
point_map[start_x][start_y].insert_time(id, 0, route_time);
point_map[end_x][end_y].insert_time(id, route_time, 0);
int route_time_plus = 0;
int route_time_minus = 0;
for (int i = 1; i < station->size() - 1; i++)
{
int dx = station->at(i).first;
int dy = station->at(i).second;
a = (dx - start_x) * (dx - start_x) + (dy - start_y) * (dy - start_y);
route_dis = (int)sqrt(a);
route_time_plus = (route_dis / speed) + 1;
a = (dx - end_x)*(dx - end_x) + (dy - end_y)*(dy - end_y);
route_dis = (int)sqrt(a);
route_time_minus = (route_dis / speed) + 1;
point_map[dx][dy].insert_time(id, route_time_plus, route_time_minus);
}
}
void output()
{
for (int i = 0; i <= vertex_num; i++)
{
for (int j = 0; j <= vertex_num; j++)
{
cout << "(" << i << "," << j << ")" << " ";
edge* p = graph_p[i].vertex[j].first_edge;
while (p != NULL)
{
cout << "(" << p->begin_point.first << "," << p->begin_point.second << ")" << "-->" << "(" << p->des_point.first << "," << p->des_point.second << ")" << ": ";
cout << " id:" << p->id << " cost:" << p->cost << " time: " << p->time<<"direct:"<<p->direct;
p = p->next;
}
cout << endl;
}
}
}
void timemap_output()
{
for (int i = 0; i <= vertex_num; i++)
{
for (int j = 0; j <= vertex_num; j++)
{
cout << "(" << i << "," << j << ")" << " ";
map<int, pair<int, int>>::iterator it;
for (it = point_map[i][j].record.begin(); it != point_map[i][j].record.end(); it++)
{
cout <<"id="<< it->first << "正向时间:" << it->second.first << "负向时间:" << it->second.second<<" ";
}
cout << endl;
}
}
}
void Dijkstra_min_price(int x, int y, int res_x, int res_y)
{
//开辟空间
int** price = NULL;//距离矩阵
int** vis = NULL;//访问矩阵
int** route_id = NULL;//路线id的前置矩阵
pair<int, int>** front_point = NULL;//前置节点的访问矩阵
price = new int* [vertex_num + 1];
vis = new int* [vertex_num + 1];
route_id = new int* [vertex_num + 1];
front_point = new pair<int, int>* [vertex_num + 1];
for (int i = 0; i <= vertex_num; i++)
{
price[i] = new int[vertex_num + 1];
vis[i] = new int[vertex_num + 1];
route_id[i] = new int[vertex_num + 1];
front_point[i] = new pair<int, int>[vertex_num + 1];
}
//初始化
for (int i = 0; i <= vertex_num; i++)
{
for (int j = 0; j <= vertex_num; j++)
{
price[i][j] = max_i;
vis[i][j] = 0;
route_id[i][j] = 0;
front_point[i][j].first = 0;
front_point[i][j].second = 0;
}
}
priority_queue<pair<int, pair<int, int>>> q;
while (!q.empty()) q.pop();
price[x][y] = 0;
route_id[x][y] = 0;
q.push(make_pair(0, make_pair(x, y)));
while (!q.empty())
{
int dx = q.top().second.first;
int dy = q.top().second.second;
int last_id = route_id[dx][dy];
q.pop();
if (vis[dx][dy] == 1) continue;
vis[dx][dy] = 1;
edge* p = graph_p[dx].vertex[dy].first_edge;
while (p != NULL)
{
int des_x = p->des_point.first;
int des_y = p->des_point.second;
int new_price = 0;
if (last_id == p->id)
new_price = 0;
else
new_price = p->cost;
if (price[des_x][des_y] > price[dx][dy] + new_price)
{
price[des_x][des_y] = price[dx][dy] + new_price;
front_point[des_x][des_y].first = dx;
front_point[des_x][des_y].second = dy;
route_id[des_x][des_y] = p->id;
q.push(make_pair(-price[des_x][des_y], make_pair(des_x, des_y)));
}
p = p->next;
}
}
if (price[res_x][res_y] == max_i)
cout << "该车站不可达" << endl;
else
printf("(%d,%d)到(%d,%d)的最小票价是%d\n", x, y, res_x, res_y, price[res_x][res_y]);
vector<pair<int, int>> vec;
int flag1 = res_x;
int flag2 = res_y;
int front_flag1 = res_x;
int front_flag2 = res_y;
while (flag1 != x || flag2 != y)
{
vec.push_back(make_pair(flag1, flag2));
front_flag1 = front_point[flag1][flag2].first;
front_flag2 = front_point[flag1][flag2].second;
flag1 = front_flag1;
flag2 = front_flag2;
}
printf("起始点(%d,%d)\n", x, y);
int dx2 = vec.at(vec.size() - 1).first;
int dy2 = vec.at(vec.size() - 1).second;
int now_id = route_id[dx2][dy2];
printf("线路%d:", now_id);
printf("(%d,%d) ", x, y);
int last_x = x;
int last_y = y;
for (int i = vec.size() - 1; i >= 0; i--)
{
if (now_id == route_id[vec.at(i).first][vec.at(i).second])
printf("(%d,%d) ", vec.at(i).first, vec.at(i).second);
else
{
now_id = route_id[vec.at(i).first][vec.at(i).second];
printf("\n在(%d,%d)换乘到线路%d:(%d,%d) ", last_x, last_y, now_id, last_x, last_y);
printf("(%d,%d) ", vec.at(i).first, vec.at(i).second);
}
last_x = vec.at(i).first;
last_y = vec.at(i).second;
}
printf("\n终结点(%d,%d)\n", res_x, res_y);
}
void Dijkstra_min_time(int x, int y, int res_x, int res_y)
{
//开辟空间
int** dis = NULL;//距离矩阵
int** vis = NULL;//访问矩阵
int** route_id = NULL;//路线id的前置矩阵
pair<int, int>** front_point = NULL;//前置节点的访问矩阵
dis = new int* [vertex_num + 1];
vis = new int* [vertex_num + 1];
route_id = new int* [vertex_num + 1];
front_point = new pair<int, int>* [vertex_num + 1];
for (int i = 0; i <= vertex_num; i++)
{
dis[i] = new int[vertex_num + 1];
vis[i] = new int[vertex_num + 1];
route_id[i] = new int[vertex_num + 1];
front_point[i] = new pair<int, int>[vertex_num + 1];
}
//初始化
for (int i = 0; i <= vertex_num; i++)
{
for (int j = 0; j <= vertex_num; j++)
{
dis[i][j] = max_i;
vis[i][j] = 0;
route_id[i][j] = 0;
front_point[i][j].first = 0;
front_point[i][j].second = 0;
}
}
priority_queue<pair<int, pair<int, int>>> q;
while (!q.empty()) q.pop();
dis[x][y] = 0;
route_id[x][y] = 0;
q.push(make_pair(0, make_pair(x, y)));
while (!q.empty())
{
int dx = q.top().second.first;
int dy = q.top().second.second;
q.pop();
if (vis[dx][dy] == 1) continue;
vis[dx][dy] = 1;
edge * p = graph_p[dx].vertex[dy].first_edge;
while (p != NULL)
{
int des_x = p->des_point.first;
int des_y = p->des_point.second;
if (dis[des_x][des_y] > dis[dx][dy] + p->time)
{
dis[des_x][des_y] = dis[dx][dy] + p->time;
front_point[des_x][des_y].first = dx;
front_point[des_x][des_y].second = dy;
route_id[des_x][des_y] = p->id;
q.push(make_pair(-dis[des_x][des_y], make_pair(des_x, des_y)));
}
p = p->next;
}
}
if (dis[res_x][res_y] == max_i)
cout << "该车站不可达" << endl;
else
printf("(%d,%d)到(%d,%d)的最短时间是%d\n", x, y, res_x, res_y, dis[res_x][res_y]);
vector<pair<int, int>> vec;
int flag1 = res_x;
int flag2 = res_y;
int front_flag1 = res_x;
int front_flag2 = res_y;
while (flag1 != x || flag2 != y)
{
vec.push_back(make_pair(flag1, flag2));
front_flag1 = front_point[flag1][flag2].first;
front_flag2 = front_point[flag1][flag2].second;
flag1 = front_flag1;
flag2 = front_flag2;
}
printf("起始点(%d,%d)\n", x, y);
int dx2 = vec.at(vec.size() - 1).first;
int dy2 = vec.at(vec.size() - 1).second;
int now_id = route_id[dx2][dy2];
printf("线路%d:", now_id);
printf("(%d,%d) ", x, y);
int last_x = x;
int last_y = y;
for (int i = vec.size() - 1; i >= 0; i--)
{
if (now_id == route_id[vec.at(i).first][vec.at(i).second])
printf("(%d,%d) ", vec.at(i).first, vec.at(i).second);
else
{
now_id = route_id[vec.at(i).first][vec.at(i).second];
printf("\n在(%d,%d)换乘到线路%d:(%d,%d) ",last_x,last_y, now_id,last_x,last_y);
printf("(%d,%d) ", vec.at(i).first, vec.at(i).second);
}
last_x = vec.at(i).first;
last_y = vec.at(i).second;
}
printf("\n终结点(%d,%d)\n", res_x, res_y);
}
void Dijkstra_min_time_wait(int x, int y, int res_x, int res_y)
{
//开辟空间
int** dis = NULL;//时间矩阵
int** vis = NULL;//访问矩阵
int** route_id = NULL;//路线id的前置矩阵
pair<int, int>** front_point = NULL;//前置节点的访问矩阵
dis = new int* [vertex_num + 1];
vis = new int* [vertex_num + 1];
route_id = new int* [vertex_num + 1];
front_point = new pair<int, int>* [vertex_num + 1];
for (int i = 0; i <= vertex_num; i++)
{
dis[i] = new int[vertex_num + 1];
vis[i] = new int[vertex_num + 1];
route_id[i] = new int[vertex_num + 1];
front_point[i] = new pair<int, int>[vertex_num + 1];
}
//初始化
for (int i = 0; i <= vertex_num; i++)
{
for (int j = 0; j <= vertex_num; j++)
{
dis[i][j] = max_i;
vis[i][j] = 0;
route_id[i][j] = 0;
front_point[i][j].first = 0;
front_point[i][j].second = 0;
}
}
priority_queue<pair<int, pair<int, int>>> q;
while (!q.empty()) q.pop();
dis[x][y] = 0;
route_id[x][y] = 0;
q.push(make_pair(0, make_pair(x, y)));
while (!q.empty())
{
int dx = q.top().second.first;
int dy = q.top().second.second;
int last_id = route_id[dx][dy];
q.pop();
if (vis[dx][dy] == 1) continue;
vis[dx][dy] = 1;
edge * p = graph_p[dx].vertex[dy].first_edge;
while (p != NULL)
{
int des_x = p->des_point.first;
int des_y = p->des_point.second;
int des_time = p->time;//走边的时间
int des_id = p->id;
int des_direct = p->direct;
int wait_time = 0;//等待时间
int need_time = 0;//从始发站到该点用的时间
if (des_id == last_id)//如果是同一条路线上的,不需要等车
{
wait_time = 0;
}
else
{
if (des_direct == 0)//边是正向
{
need_time = point_map[dx][dy].record[des_id].first;
}
else if (des_direct == 1)//边是负向
{
need_time = point_map[dx][dy].record[des_id].second;
}
if (dis[dx][dy] >= need_time)//到这个点的时候,第一班车已经走了
{
wait_time = (dis[dx][dy] - need_time) % wait_map[des_id];
}
else//到这个点的时候,车还没到
{
wait_time = need_time - dis[dx][dy];
}
}
if (dis[des_x][des_y] > dis[dx][dy] + des_time + wait_time)
{
dis[des_x][des_y] = dis[dx][dy] + des_time + wait_time;
front_point[des_x][des_y].first = dx;
front_point[des_x][des_y].second = dy;
route_id[des_x][des_y] = p->id;
q.push(make_pair(-dis[des_x][des_y], make_pair(des_x, des_y)));
}
p = p->next;
}
}
if (dis[res_x][res_y] == max_i)
cout << "该车站不可达" << endl;
else
printf("(%d,%d)到(%d,%d)的最短时间是%d\n", x, y, res_x, res_y, dis[res_x][res_y]);
vector<pair<int, int>> vec;
int flag1 = res_x;
int flag2 = res_y;
int front_flag1 = res_x;
int front_flag2 = res_y;
while (flag1 != x || flag2 != y)
{
vec.push_back(make_pair(flag1, flag2));
front_flag1 = front_point[flag1][flag2].first;
front_flag2 = front_point[flag1][flag2].second;
flag1 = front_flag1;
flag2 = front_flag2;
}
printf("起始点(%d,%d)\n", x, y);
int dx2 = vec.at(vec.size() - 1).first;
int dy2 = vec.at(vec.size() - 1).second;
int now_id = route_id[dx2][dy2];
printf("线路%d:", now_id);
printf("(%d,%d) ", x, y);
int last_x = x;
int last_y = y;
for (int i = vec.size() - 1; i >= 0; i--)
{
if (now_id == route_id[vec.at(i).first][vec.at(i).second])
printf("(%d,%d) ", vec.at(i).first, vec.at(i).second);
else
{
now_id = route_id[vec.at(i).first][vec.at(i).second];
printf("\n在(%d,%d)换乘到线路%d:(%d,%d) ", last_x, last_y, now_id, last_x, last_y);
printf("(%d,%d) ", vec.at(i).first, vec.at(i).second);
}
last_x = vec.at(i).first;
last_y = vec.at(i).second;
}
printf("\n终结点(%d,%d)\n", res_x, res_y);
}
};
int main()
{
vector<pair<int, int>>* route_station;
route_station = new vector<pair<int, int>>;
int point_num;//点范围
cin >> point_num;
Graph a(point_num);
int line_num;//路线数
cin >> line_num;
for (int i = 0; i < line_num; i++)
{
int station_num = 0;//站点数量
cin >> station_num;
int x, y;
route_station->clear();
for (int i = 0; i < station_num; i++)
{
cin >> x;
cin >> y;
route_station->push_back(make_pair(x, y));
}
int cin_value;
int cin_speed;
int wait_time;
int cin_id;
cout << "id:";
cin >> cin_id;
cout << "value:";
cin >> cin_value;
cout << "speed:";
cin >> cin_speed;
cout << "wait time:";
cin >> wait_time;
a.add_line(route_station, cin_value, cin_speed,wait_time,cin_id);
}
//a.output();
//a.timemap_output();
cout << "最快时间模式(不考虑等车)" << endl;
cout << "输入起点" << endl;
int begin_x1, begin_y1, desti_x1, desti_y1;
cin >> begin_x1 >> begin_y1;
cout << "输入终点"<<endl;
cin >> desti_x1 >> desti_y1;
a.Dijkstra_min_time(begin_x1, begin_y1, desti_x1, desti_y1);
cout << "最快时间模式(考虑等车)" << endl;
cout << "输入起点" << endl;
int begin_x3, begin_y3, desti_x3, desti_y3;
cin >> begin_x3 >> begin_y3;
cout << "输入终点" << endl;
cin >> desti_x3 >> desti_y3;
a.Dijkstra_min_time_wait(begin_x3, begin_y3, desti_x3, desti_y3);
cout << "最小花费模式" << endl;
cout << "输入起点" << endl;
int begin_x2, begin_y2, desti_x2, desti_y2;
cin >> begin_x2 >> begin_y2;
cout << "输入终点" << endl;
cin >> desti_x2 >> desti_y2;
a.Dijkstra_min_price(begin_x2, begin_y2, desti_x2, desti_y2);
return 0;
}