(本科的课程设计,终于从草稿箱翻出来了
目录
题目要求:
- 根据公交线路的输入格式,定义并建立合适的图模型。
- 针对输入的公交线路,能查询任何两个站点之间最便宜的路径,即输入站名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元。
当时的实验报告
1. 需求描述
1.1 问题描述
最短路径问题是图论中的一个经典问题,其中的Dijkstra算法一直被认为是图论中的好算法,但有的时候需要适当的调整Dijkstra算法才能完成多种不同的优化路径的查询。
对于某城市的公交线路,乘坐公交的顾客希望在这样的线路上实现各种优化路径的查询。设该城市的公交线路的输入格式为:
线路编号:起始站点(该站坐标);经过的站点1名(该站坐标);经过的站点2名(该站坐标);……;经过的站点n名(该站坐标);终点站名(该站坐标)。该线路的乘坐价钱。该线路平均经过多少时间来一趟。车速。
例如:63:A(32,,45);B(76,45);C(76,90);……;N(100,100)。1元。5分钟。1/每分钟。
假定该线路的乘坐价格与乘坐站数无关,假定不考虑公交线路上的交通堵塞。对这样的公交线路,需要在其上进行的优化路径查询包括:任何两个站点之间最便宜的路径;任何两个站点之间最省时间的路径等等。
1.2 基本要求
1)根据上述公交线路的输入格式,定义并建立合适的图模型
2)针对上述公交线路,能查询任何两个站点之间最便宜的路径,即输入站名S,T后,可以输出从S到T的最便宜路径,输出格式为:线路x:站名S,…,站名M1;换乘线路x:站名M1,…,站名M2;换乘线路x:站名MK,…,站名T。共花费x元。
3)针对上述公交线路,能查询获得任何两个站点之间最省时间的路径(不考虑在中间站等下一辆线路的等待时间),即输入站名S,T后,可以输出从S到T的不考虑在中间站等下一辆线路的等待时间的最省时间的路径,输出格式为:线路x:站名S,…,站名M1;换乘线路x:站名M1,…,站名M2;换乘线路x:站名MK,…,站名T。共花费x时间。
4)针对上述公交线路,能查询获得任何两个站点之间最省时间的路径(要考虑在中间站等下一辆线路的等待时间),即输入站名S,T后,可以输出从S到T的考虑在中间站等待下一辆线路的最省时间的路径,输出格式为:线路x:站名S,…,站名M1;换乘线路x:站名M1,…,站名M2;换乘线路x:站名MK,…,站名T。共花费x元。
5)实现提示:需深入考虑,应根据不同的应用目标,即不同的优化查询来建立合适的图模型。
1.3 输入要求
输入形式:
线路编号:起始站点(该站坐标);经过的站点1名(该站坐标);经过的站点2名(该站坐标);……;经过的站点n名(该站坐标);终点站名(该站坐标)。该线路的乘坐价钱。该线路平均经过多少时间来一趟。车速。
输入数据例子:
111:A(1,1);B(3,4);C(5,2);D(4,6)。4元。1分钟。4/分钟。
……
1.4 输出要求
输出形式:
线路x:站名S,…,站名M1;换乘线路x:站名M1,…,站名M2;换乘线路x:站名MK,…,站名T。共花费x元。
线路x:站名S,…,站名M1;换乘线路x:站名M1,…,站名M2;换乘线路x:站名MK,…,站名T。共花费x时间。
输出数据例子:
线路222:站点A,站点E。换乘线路333:站点E,站点D。共花费2元。
2. 设计
2.1 结构设计
- 公交车站点struct busNode:包含每个站点的名称sta_name、横坐标dx、纵坐标dy、指向下一个站点指针*next;
- 公交车类class busNode:存储了公交车经过的站点链表结构;
- 公交车线路类busRoute:存储公交车经过的站点、票价、速度、发车时间间隔;
- 两站点之间的线路信息struct routeInfo:包括线路编号和数据(票价或者所用时间);
- 两站点之间的线路信息,可有多条线路struct ver:成员对象为routeInfo类型的数组;
- 邻接矩阵类adjMatrix:存储整合后的公交线路信息,同时实现求最短路径的Dijkstra算法以及输出最短路径方法。
2.2 数据及数据类(型)定义
1. 公交车节点busNode
struct busNode {
string sta_name;//站点名称
double dx;//站点横坐标
double dy;//站点纵坐标
busNode *next;//指向下一个站点的指针
};
2. 公交车经过的站点链表类busChain
class busChain {
friend class busRoute;//友元
public:
busChain() {//构造函数
firstNode = NULL;
listSize = 0;
}
~busChain();//析构函数
bool isEmpty() { return listSize == 0; }//判断是否为空
int size() { return listSize; }//站点数目
busNode *getFirst() { return firstNode; }//得到首节点
string getFirstStation() { return firstNode->sta_name; }//得到始发站名称
void insert(const string &s, const double &x, const double &y);//在链表最后插入一个节点
void get_distance();//计算每两个站点之间的距离,存入dx
void output(ostream &out) const;//输出
private:
busNode * firstNode;//指向首节点的指针
int listSize;//节点个数
};
3. 公交车线路类busRoute
class busRoute {
public:
busRoute();//构造函数
~busRoute();//析构函数
void initi1(int nn, int ss);//设置线路编号、站点总数
void initi2(double sp, double pp, double tt);//设置票价、发车间隔、速度
double getPrice() { return price; }//得到线路票价
double getSpeed() { return speed; }//得到速度
int getNum() { return num; }//得到线路编号
int getSum() { return sum; }//得到站点总数
double getTime_() { return time_; }//得到发车时间间隔
busChain& getLink() { return link; }//得到经过站点链表
void output(ostream &out) const;//输出本线路停经站点及其坐标
private:
int num;//线路编号
int sum;//站点总数
double speed;//速度
double price;//票价
double time_;//发车时间间隔
busChain link;//本公交线路链表
};
4. 两站点之间的线路信息struct routeInfo
struct routeInfo {
int Info;//线路编号
double data;//数据
};
5. 两站点之间的线路信息,可有多条线路struct ver
struct ver {
routeInfo ro[sn];//每条线路信息
};
6. 邻接矩阵类class adjMatrix
class adjMatrix {
public:
adjMatrix(int theCapacity);//构造函数
~adjMatrix();//析构函数
void insert(int i, int j, int nth, routeInfo rt);//插入元素
void erase(int i, int j, int nth);//删除元素
double getElement(int i, int j, int nth);//查找
int getM() const {//得到总边数
return m;
}
void ini();//初始化为无边
int search(int i, int j, int su);//查找站点i到站点j的所有线路中的最优解
int search2(int i, int j, int su, busRoute *bus);//查找站点i到站点j的所有线路中的最优解(判断是否加入等待时间)
void Dijkstra(double *dis, int *pre, int da, int su);//求da到其他点的最短路径
void outputPath(int pa, int pb, string *in, int *pre, int su);//输出pa到pb的最短线路
void Dijkstra2(double *dis, int *pre, int da, int su, busRoute *bus); //求da到其他点的最短路径,加入中间站点的等待时间
double find_(int Infomation, int su, busRoute *bus);//查找线路编号对应的等待时间
private:
ver * *theMatrix;//邻接矩阵
int n;//点数
int m;//边数
};
7. 全局变量
#define sn 30//站点总数
#define noEdge 9999
int option; //进行功能选择
char flag = 'y';//通过输入判断是否退出
busRoute *bus = new busRoute[10];//公交车线路数组,预设为10,可修改
int BUS_SUM;//公交线路数
string in[sn] =
{","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"," "," "," " }; //已有对应关系,将站点名和站点标号对应
double dis[sn];//到源点的距离
int vis[sn];//Dijkstra算法中判断点是否访问过
int pre[sn];//前驱,Dijkstra算法中用来记录前驱
adjMatrix matrix(30);//邻接矩阵
2.3 算法设计及分析-伪代码
2.3.1 公交车经过的站点链表类busChain
1. 构造函数:
busChain::busChain() {//构造函数
firstNode指向空;
listSize置为0;
}
2. 析构函数:
busChain::~busChain() {
busNode *p = firstNode, *pp;
while (p不为空) {
pp = p->next;
删除p节点;
p = pp;
}
}
3. 在链表的最后插入一个节点:
void insert(站点名称, 横坐标, 纵坐标) {
if(firstNode为空) {//当前链表为空
firstNode = new busNode;
firstNode->sta_name = 站点名称;
firstNode->dx = 横坐标;
firstNode->dy = 纵坐标;
firstNode->next = 空;
}
else {
busNode *p = firstNode;
if(p->next不为空)
p=p->next;
busNode *pp = new busNode;
pp->sta_name = 站点名称;
pp->dx = 横坐标;
pp->dy = 纵坐标;
pp->next = 空;
p-next = pp;
}
listSize+1;
}
4. 计算相邻两个站点之间的距离并将其值存入dx:
void busChain::get_distance() {
busNode *p = firstNode, *pp;
for (pp = p->next; pp不为空; pp = pp->next) {
p->dx = 距离;
p = pp;
}
p->dx = noEdge;//最后一个节点指针指向空
}
5. 输出本线路信息:
void busChain::output(ostream &out) const {
busNode *p;
for (p = firstNode; p->next不为空; p = p->next) {
输出站点p的名称(横坐标,纵坐标);
}
输出最后一个站点的名称(横坐标,纵坐标)。
}
6. 重载<<:
ostream &operator<<(ostream &out, const busChain &theChain) {
theChain.output(out);
return out;
}
2.3.2 公交车线路类busRoute
1. 构造函数:
busRoute() {
将线路编号、站点总数、速度、价格、发车间隔均置为0;
链表的头节点指向空;
}
2. 析构函数:
busRoute::~busRoute() {
将线路编号、站点总数、速度、价格、发车间隔均置为0;
调用链表的析构函数;
}
3. 输入线路编、站点总数、各站点名称及其坐标函数:
void busRoute::initi1(int nn, int ss) {//nn表示线路编号,ss表示站点总数
线路编号 = nn;
站点总数 = ss;
依次输入每个站点的名称、横坐标、纵坐标并将其插入链表中;
}
4. 输入线路票价、发车间隔、速度函数:
void busRoute::initi2(double sp, double pp, double tt) {
//sp表示价格,pp表示发车间隔,tt表示速度
价格 = sp;
发车间隔 = pp;
速度 = tt;
}
5. 输出函数:
void busRoute::output(ostream &out) {//输出本线路停经站点及其坐标
输出:线路编号+链表;
调用get_distance()方法计算相邻两个站点之间的距离,将结果存入dx;
输出价格+发车间隔+速度;
}
6. 重载<<:
ostream &operator<<(ostream &out, busRoute &theRoute) {
theRoute.output(out);
return out;
}
2.3.3 邻接矩阵类adjMatrix
1. 构造函数:
adjMatrix::adjMatrix(int theCapacity) {
点数 = theCapacity - 1;
创建二维数组theMatrix;
利用三层循环将二维数组每个元素的每条线路对应的数据data置为noEdge;
边数 = 0;
}
2. 析构函数:
adjMatrix::~adjMatrix() {
删除数组theMatrix;
边数= 0;
点数= 0;
}
3. 插入函数:复杂度O(1)
void adjMatrix::insert(int i, int j, int nth, routeInfo rt) {//在第i行第j列处的第nth条线路信息中插入rt
if (第i行第j列处的第nth条线路对应的data == noEdge) {
将rt插入第i行第j列处的第nth条线路处;
边数 ++;
}
else {//已有边,更新信息
theMatrix[i][j].ro[nth] = rt;
//边数不作操作
}
}
4. 删除函数:复杂度O(1)
void adjMatrix::erase(int i, int j, int nth) {//删除第i行第j列处的第nth条线路的边
if (第i行第j列处的第nth条线路对应的data == noEdge) {
输出报错信息;
}
else {
第i行第j列处的第nth条线路对应的data = noEdge;
边数 --;
}
}
5. 取值函数:复杂度O(1)
double adjMatrix::getElement(int i, int j, int nth) {//得到第i行第j列处的第nth条线路的边值
return theMatrix[i][j].ro[nth].data;
}
6. 初始化函数:
void adjMatrix::ini() {//初始化均为无边,线路编号均为0
利用3层循环将每一个元素对应个每一条线路编号置为0,data置为noEdge;
}
7. Dijkstra算法函数:复杂度O(26*26)
void adjMatrix::Dijkstra(double *dis, int *pre, int da, int su) {//求da到其他所有点的最短路径长度
创建int *vis标记数组并初始化为0表示未访问过;
将所有的点循环一次找出直接与da相连的点i,①将其对应的dis数组中的值置为theMatrix[da][i].ro[search(da,i,su)].data,其中search(da,i,su)函数表示返回从点da到i的所有线路中对应data值最小的线路的索引值 ②将其前驱pre[i]设为da ③;
标记dis[da] = 0,表示da到da距离为0;
标记vis[da] = 1,表示da已经访问过;
for (循环遍历所有的点) {
double temp = noEdge;
int v = 0;
for (循环所有的点j) {//找出距离 点集 最近的点
if (j没有访问过 && dis[j]<temp) {
v = j;
temp = dis[j];
}
}
标记vis[v] = 1;
for (循环所有点j) {
if (j没有访问过 && 点v到点j的最优线路对应的数据 < noEdge) {
double dd = dis[v] +点v到点j的最优线路对应的数据theMatrix[v][j].ro[search(v, j, su)].data;
if (dd < dis[j]) {//更新
dis[j] = dd;
设置pre[j] = v,表示j的前驱为v;
}
}
}
}
}
8. 输出路径函数:从终点pb开始向前,每次向前寻找前驱,即可得到路径
void adjMatrix::outputPath(int pa, int pb, string *in, int *pre, int su) {//输出从pa到pb的最短路径
if (pa == pb)
return;
创建temp数组存储要输出的站点编号;
计数器co = 1;
temp[co] = pb;//终点
co ++;
te = pb的前驱;
while (te != pa) {//将路径上的点存到temp数组中
temp[co] = te;
co ++;
te = pre[te];//更新前驱
}
temp[co] = pa;
int info = 最后两个站点之间的最优线路的编号;
cout << "线路" << info << ":";
for (int i = co; i >= 1; i--) {//倒序输出
if (i != 1) {
if (当前两个站点之间的线路编号 != info) {
cout << "站点" << in[temp[i]] << "。";
cout << "换乘线路" << theMatrix[temp[i]][temp[i - 1]].ro[search(temp[i], temp[i - 1], su)].Info << ":";
cout << "站点" << in[temp[i]] << ",";
}
else {
cout << "站点" << in[temp[i]] << ",";
}
info = 当前线路编号;//更新当前线路信息
}
else {//最后一个站点
cout << "站点" << in[temp[i]] << "。" << endl;
}
}
}
9. 考虑是中间站点等待时间的Dijkstra算法函数:复杂度O(26*26)
void adjMatrix::Dijkstra2(double *dis, int *pre, int da, int su, busRoute *bus) {//求da到其他所有点的最短路径长度,加入等待时间
创建int *vis标记数组并初始化为0表示未访问过;
将所有的点循环一次找出直接与da相连的点i,①将其对应的dis数组中的值置为theMatrix[da][i].ro[search(da,i,su)].data,其中search(da,i,su)函数表示返回从点da到i的所有线路中对应data值最小的线路的索引值,在这里因为是出发的站点所以不需要考虑等待时间 ②将其前驱pre[i]设为da;
标记dis[da] = 0,表示da到da距离为0;
标记vis[da] = 1,表示da已经访问过;
tt = 1;
for (循环遍历所有点) {
double temp = noEdge;
int v = 0;
for (循环遍历所有点j) {//找出距离 点集 最近的点
if (j没有访问过 && dis[j]<temp) {
v = j;
temp = dis[j];
}
}
标记vis[v] = 1,表示v已经访问过;
int oldInfo;
if (tt == 1) {
oldInfo = theMatrix[da][v].ro[search(da, v, su)].Info;//oldInfo初始赋值,表示线路编号,与之后加入的站点的线路编号比较判断是否同一线路,来确定是否加入等待时间
}
tt = 2;
for (循环遍历所有点) {
if (oldInfo == 当前两站点之间加入等待时间的最优线路对应的线路编号,这里求最优线路的索引时调用的是search2()函数) {//相同线路,不需加入等待时间
if (j没有访问过 && 点v到点j的加入等待时间的最优线路对应的数据theMatrix[v][j].ro[search2(v, j, su, bus)].data < noEdge) {//更新
double dd = dis[v] + 点v到点j的加入等待时间的最优线路对应的数据theMatrix[v][j].ro[search2(v, j, su, bus)].data;
if (dd < dis[j]) {//更新
dis[j] = dd;
设置pre[j] = v,表示j的前驱是v;
}
}
}
else {//不同线路,需要加入等待时间
if (没有访问过 && 点v到点j的加入等待时间的最优线路对应的数据 + 对应等待时间(调用find_()函数)< noEdge) {//更新
double dd = dis[v] +点v到点j的加入等待时间的最优线路对应的数据theMatrix[v][j].ro[search2(v, j, su,bus)].data +对应等待时间find_(theMatrix[v][j].ro[search2(v, j, su, bus)].Info, su, bus);
if (dd < dis[j]) {//更新
dis[j] = dd;
设置pre[j] = v,表示j的前驱是v;
}
oldInfo = theMatrix[v][j].ro[search2(v, j, su, bus)].Info;//oldInfo更新
}
}
}
}
}
10. 找出站点i到站点j的所有线路中的最优解:复杂度O(su)
int adjMatrix::search(int i, int j, int su) {//找出站点i到站点j的所有公交线路中的最优解 ,su表示两站点之间的线路条数,返回线路编号对应的索引值
double qq = 站点i到站点j的第一条线路的数据theMatrix[i][j].ro[1].data;
int uu = 1;
for (int k = 2; k <= su; k++) {
if (theMatrix[i][j].ro[k].data < qq) {//更新
qq = theMatrix[i][j].ro[k].data;
uu = k;
}
}
return uu;
}
11. 查找线路编号对应的等待时间:复杂度O(su)
double adjMatrix::find_(int Information, int su, busRoute *bus) { //查找线路编号为Information的线路对应的等待时间
if (Information == 0)
return 0;
for (int i = 1; I <= su; i++) {
if (bus[i]的线路编号 == Information)
return bus[i]的等待时间;
}
}
12. 找出站点i到站点j的所有公交线路中的最优解(加上等待时间),返回值为线路对应的索引:复杂度O(su)
int adjMatrix::search2(int i, int j, int su,busRoute *bus) {//找出站点i到站点j的所有公交线路中的最优解(加上等待时间) ,su表示两站点之间的线路条数,返回值为线路对应的索引值
double qq = 站点i到站点j的第一条线路的数据theMatrix[i][j].ro[1].data + 站点i到站点j的第一条线路对应的等待时间find_(theMatrix[i][j].ro[1].Info,su,bus);
int uu = 1;
for (int k = 2; k <= su; k++) {
if (theMatrix[i][j].ro[k].data + find_(theMatrix[i][j].ro[k].Info, su, bus) < qq) {//更新
qq = theMatrix[i][j].ro[k].data+find_(theMatrix[i][j].ro[k].Info,su,bus);
uu = k;
}
}
return uu;
}
2.3.4 cpp文件中:
1. 查找最少花费函数中的建立票价邻接矩阵部分:因为一条线路上任意两个站点之间的票价都是一样的,所以需要设置双层循环,线路上的任意两点之间都要加入边
矩阵调用matrix.ini()进行初始化;
routeInfo rI;
//建立票价邻接矩阵
for (循环BUS_SUM次,BUS_SUM表示输入的线路数) {
rI.Info = 第i条线路的编号;
rI.data = 第i条线路的价格;
busNode *p = 第i条线路对应的链表的头节点;
for (p; p != NULL; p = p->next) {
busNode *pp = p->next;
int ta = 站点名为p->sta_name的站点标号;
for (pp; pp != NULL; pp = pp->next) {
int tb = 站点名为pp->sta_name的站点标号;
在矩阵matrix的第ta行第tb列的第i条线路处插入数据rI;
在矩阵matrix的第tb行第ta列的第i条线路处插入数据rI;
}
}
}
2. 查询最短时间(不考虑中间站点的等待时间)函数中建立邻接链表部分:只在相邻两站点之间加入边
矩阵调用matrix.ini()进行初始化;
routeInfo rI;
//建立耗费时间邻接矩阵
for (循环BUS_SUM次) {
rI.Info = 第i条线路的编号;
busNode *p = 第i条线路对应的链表的头节点;
for (p; p != NULL; p = p->next) {
busNode *pp = p->next;
int ta =站点名为p->sta_name的站点标号;
if (pp != NULL) {
double ddis = p节点的dx值;
rI.data = ddis / 第i条线路的速度;//得到时间
int tb = 站点名为pp->sta_name的站点标号;
在矩阵matrix的第ta行第tb列的第i条线路处插入数据rI;
在矩阵matrix的第tb行第ta列的第i条线路处插入数据rI;
}
}
}
3. 查找最少时间(考虑中间站点的等待时间)函数中建立邻接链表部分同上,之后调用Dijkstra2方法
4. 输出耗费时间或花费结果时,使用如下语句格式化输出两位小数:
cout << "共花费" << setiosflags(ios::fixed) << setprecision(2) << dis[getIndex(sb)] << "分钟时间。" << endl << endl;
代码
1) main.cpp
// 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <iostream>
#include "busChain.h"
#include "busRoute.h"
#include "adjMatrix.h"
#include <string.h>
#include <string>
#include <iomanip>
#include <cmath>
#define sn 30//站点总数
#define noEdge 9999
using namespace std;
//函数声明
void Welcome();
void Input();
void Menu();
void getRoutes();
int choose();
void Function();
void findMinCost();
void findMinTime1();
void findMinTime2();
void Dijkstra(int da);
int getIndex(string bs);
void outputPath(int pa, int pb);
void Dijkstra2(int da);
int find_(int Infomation);
//全局变量
int option;//进行功能选择
char flag = 'y';//每个功能完成后判断是否退出
busRoute *bus = new busRoute[10];//公交车线路数组
int BUS_SUM;//公交线路数
string in[sn] = { " ","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"," "," "," " };
//已有对应关系,将站点名和站点标号对应
int vis[sn];//判断是否访问过
int pre[sn];//前驱
double dis[sn];//到源点的距离
adjMatrix matrix(30);//邻接矩阵
int main() {
Welcome();
Input();
getRoutes();
return 0;
}
//欢迎界面
void Welcome() {
cout << "\n\n";
cout << "\t* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\t\n";
cout << "\t*\t\t\t\t\t\t\t\t*\n";
cout << "\t*\t\t欢迎使用公交线路优化查询系统\t\t\t*\n";
cout << "\t*\t\t\t\t\t\t\t\t*\n";
cout << "\t*\t\t(当前已覆盖站点:A-Z)\t\t\t\t*\n";
cout << "\t*\t\t\t\t\t\t\t\t*\n";
cout << "\t* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\t\n";
cout << endl << endl;
cout << "=------------------------------------------------------------------------------=\n";
}
//输入线路信息
void Input() {
cout << "请输入线路总条数:";
cin >> BUS_SUM;
int n, nn;
for (int i = 1; i <= BUS_SUM; i++) {
cout << "请输入第" << i << "条线路的编号:";
cin >> nn;
cout << "请输入第" << i << "条线路的站点个数:";
cin >> n;
bus[i].initi1(nn, n);
cout << "请输入第" << i << "条线路的票价(元)、发车间隔(分钟)、速度(每分钟),以空格分隔:";
int tt;
double xp, yp;
cin >> xp >> yp >> tt;
bus[i].initi2(xp, yp, tt);//设置票价、发车间隔、速度
cout << endl;
}
}
//显示已有公交线路
void getRoutes() {
cout << endl;
cout << "=------------------------------------------------------------------------------=\n";
cout << "目前已知公交线路相关信息如下:" << endl;
for (int i = 1; i <= BUS_SUM; i++) {
cout << bus[i];
}
cout << "=------------------------------------------------------------------------------=\n";
cout << "是否确认当前信息?(y/n)";//是否有错
char aaa;
cin >> aaa;
cout << endl;
if (aaa == 'y') {
Menu();
return;
}
else {
cout << "请重新输入公交线路信息!" << endl;
for (int i = 1; i <= BUS_SUM; i++) {
cout << "cpp " << i << endl;
bus[i].getLink().~busChain();
}
//cout << "???"<<endl;
cout << "=------------------------------------------------------------------------------=\n";
Input();
getRoutes();
}
}
//菜单
void Menu() {
while (flag == 'y') {
cout << "\n\n";
cout << "\t* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\t\n";
cout << "\t*\t\t\t\t\t\t\t\t*\n";
cout << "\t*\t\t欢迎使用公交线路优化查询系统\t\t\t*\n";
cout << "\t* _____________________________________________________________ *\t\n";
cout << "\t* ------------------------------------------------------------- *\t\n";
cout << "\t*\t\t\t\t\t\t\t\t*\n";
cout << "\t*\t\t\t\t\t\t\t\t*\n";
cout << "\t* \t使用帮助:请查看下列菜单输入项目对应标号进入查询。\t*\n";
cout << "\t* ------------------------------------------------------------- *\t\n";
cout << "\t*\t\t\t\t\t\t\t\t*\n";
cout << "\t*\t\t1-查询两站点最省钱方案;\t\t\t*\t\n";
cout << "\t*\t\t\t\t\t\t\t\t*\n";
cout << "\t*\t\t2-查询两站点最省时方案(不考虑等待时间);\t*\t\n";
cout << "\t*\t\t\t\t\t\t\t\t*\n";
cout << "\t*\t\t3-查询两站点最省时方案(考虑等待时间);\t*\t\n";
cout << "\t*\t\t\t\t\t\t\t\t*\n";
cout << "\t*\t\t4-退出。\t\t\t\t\t*\t\n";
cout << "\t*\t\t\t\t\t\t\t\t*\n";
cout << "\t* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\t\n";
cout << endl << endl;
Function();//选择功能
}
}
//功能
void Function() {
cout << "=------------------------------------------------------------------------------=\n";
cout << "\t请输入你的选择:";
cin >> option;
cout << "=------------------------------------------------------------------------------=\n";
if (option == 1)
findMinCost();//查找最少花费
else if (option == 2)
findMinTime1();//查找最少时间(不考虑等待时间)
else if(option==3)
findMinTime2();//查找最少时间(考虑等待时间)
else if(option==4) {
cout<<"\t已退出“公交路径查询系统”!"<<endl;
flag='n';
return;//返回,结束
}
else {
cout << "\t您输入的指令有误!\n\n\n";
cout << "___________________________________________________________________________________\n";
cout << "-----------------------------------------------------------------------------------\n\n";
//Menu();
}//错误情况-容错性?
}
//查找最少花费
void findMinCost() {
cout << "正在使用Function1-查询两站点最省钱方案;" << endl;
cout << "=------------------------------------------------------------------------------=\n";
matrix.ini();
routeInfo rI;
//建立票价邻接矩阵
for (int i = 1; i <= BUS_SUM; i++) {
rI.Info = bus[i].getNum();
rI.data = bus[i].getPrice();
busNode *p = bus[i].getLink().getFirst();
for (p; p != NULL; p = p->next) {
busNode *pp = p->next;
int ta = getIndex(p->sta_name);
for (pp; pp != NULL; pp = pp->next) {
int tb = getIndex(pp->sta_name);
matrix.insert(ta, tb, i, rI);
matrix.insert(tb, ta, i, rI);
//取最短的边
}
}
}
string sa, sb;
cout << "请输入出发站点和目的站点(以空格分隔):";
cin >> sa >> sb;
matrix.Dijkstra(dis, pre, getIndex(sa), BUS_SUM);
if (dis[getIndex(sb)] == noEdge) {
cout << "无法利用目前已有线路从" << sa << "到达" << sb << "!" << endl;
cout << "=------------------------------------------------------------------------------=\n";
}
else {
matrix.outputPath(getIndex(sa), getIndex(sb), in, pre, BUS_SUM);
cout << "共花费" << setiosflags(ios::fixed) << setprecision(0) << dis[getIndex(sb)] << "元。" << endl << endl;
}
cout << "=------------------------------------------------------------------------------=\n";
return;
}
//查找最少时间(不考虑等待时间)
void findMinTime1() {
cout << "正在使用Function2-查询两站点最省时方案(不考虑等待时间);" << endl;
cout << "=------------------------------------------------------------------------------=\n";
matrix.ini();
routeInfo rI;
//建立耗费时间邻接矩阵
for (int i = 1; i<=BUS_SUM; i++) {
rI.Info = bus[i].getNum();
busNode *p = bus[i].getLink().getFirst();
for (p; p != NULL; p = p->next) {
busNode *pp = p->next;
int ta = getIndex(p->sta_name);
if (pp != NULL) {
double ddis = p->dx;
rI.data = ddis / bus[i].getSpeed();//时间
//cout<<rI.data<<endl;
int tb = getIndex(pp->sta_name);
matrix.insert(ta, tb, i, rI);
matrix.insert(tb, ta, i, rI);
}
}
}
string sa, sb;
cout << "请输入出发站点和目的站点(以空格分隔):";
cin >> sa >> sb;
matrix.Dijkstra(dis, pre,getIndex(sa), BUS_SUM);
if (dis[getIndex(sb)] == noEdge) {
cout << "无法利用目前已有线路从" << sa << "到达" << sb << "!" << endl;
cout << "=------------------------------------------------------------------------------=\n";
}
else {
matrix.outputPath(getIndex(sa), getIndex(sb), in, pre, BUS_SUM);
//outputPath(getIndex(sa),getIndex(sb));
cout << "共花费" << setiosflags(ios::fixed) << setprecision(2) << dis[getIndex(sb)] << "分钟时间。" << endl << endl;
}
cout << "=------------------------------------------------------------------------------=\n";
return;
}
//查找最少时间(考虑等待时间)
void findMinTime2() {
cout<<"正在使用Function3-查询两站点最省时方案(考虑等待时间);"<<endl;
cout<<"=------------------------------------------------------------------------------=\n";
matrix.ini();//初始化
routeInfo rI;
//建立耗费时间(包括等待时间)邻接矩阵
for(int i=1;i<=BUS_SUM;i++) {
rI.Info=bus[i].getNum();
busNode *p=bus[i].getLink().getFirst();
for(p;p!=NULL;p=p->next) {
busNode *pp=p->next;
int ta=getIndex(p->sta_name);
if(pp!=NULL) {
double ddis=p->dx;
rI.data=ddis/bus[i].getSpeed();//时间
//cout<<rI.data<<endl;
int tb=getIndex(pp->sta_name);
matrix.insert(ta, tb, i, rI);
matrix.insert(tb, ta, i, rI);
}
}
}
/*cout<<endl;Dijkstra2(3);
for(int i=1;i<=26;i++) {
cout<<showpoint<<dis[i]<<" ";
}*/
string sa,sb;
cout<<"请输入出发站点和目的站点(以空格分隔):";
cin>>sa>>sb;
matrix.Dijkstra2(dis,pre,getIndex(sa),BUS_SUM,bus);
if(dis[getIndex(sb)]==noEdge) {
cout<<"无法利用目前已有线路从"<<sa<<"到达"<<sb<<"!"<<endl;
cout<<"=------------------------------------------------------------------------------=\n";
} else {
matrix.outputPath(getIndex(sa),getIndex(sb),in,pre,BUS_SUM);
//outputPath(getIndex(sa),getIndex(sb));
cout<<"共花费"<<setiosflags(ios::fixed)<<setprecision(2)<<dis[getIndex(sb)]<<"分钟时间。"<<endl<<endl;
}
cout<<"=------------------------------------------------------------------------------=\n";
return;
}
//得到各站点索引
int getIndex(string bs) {
for (int i = 1; i <= 26; i++) {
if (in[i] == bs)
return i;
}
return -1;
}
2) busChain.h
#ifndef busChain_
#define busChain_
#include<iostream>
#include<string>
#include<cmath>
#define noEdge 9999
using namespace std;
struct busNode {
string sta_name;//站点名称
double dx;//站点横坐标
double dy;//站点纵坐标
busNode *next;//指向下一个站点的指针
};
//公交车链表
class busChain {
friend class busRoute;//友元
public:
busChain() {//构造函数
firstNode = NULL;
listSize = 0;
}
~busChain();//析构函数
bool isEmpty() { return listSize == 0; }//判断是否为空
int size() { return listSize; }//站点数目
busNode *getFirst() { return firstNode; }//得到首节点
string getFirstStation() { return firstNode->sta_name; }//得到始发站
void insert(const string &s, const double &x, const double &y);//在链表最后插入一个节点
void get_distance();//计算每两个站点之间的距离,存入dx
void output(ostream &out) const;//输出
private:
busNode * firstNode;//指向首节点的指针
int listSize;//节点个数
};
//析构函数
busChain::~busChain() {
cout << "111" << endl;
busNode *p = firstNode, *pp;
while (p != NULL) {
pp = p->next;
delete p;
p = pp;
}
}
//在最后插入一个站点
void busChain::insert(const string &s, const double &x, const double &y) {
if (firstNode == NULL) {
firstNode = new busNode;
firstNode->sta_name = s;
firstNode->dx = x;
firstNode->dy = y;
firstNode->next = NULL;
}
else {
busNode *p = firstNode;
while (p->next != NULL) {
p = p->next;
}
busNode *pp = new busNode;
pp->sta_name = s;
pp->dx = x;
pp->dy = y;
p->next = pp;
pp->next = NULL;
}
listSize++;
//return *this;
}
void busChain::get_distance() {
busNode *p = firstNode, *pp;
for (pp = p->next; pp != NULL; pp = pp->next) {
p->dx = sqrt(abs(p->dx - pp->dx)*abs(p->dx - pp->dx) + abs(p->dy - pp->dy)*abs(p->dy - pp->dy));
p = pp;
}
p->dx = noEdge;
}
void busChain::output(ostream &out) const {
busNode *p;
for (p = firstNode; p->next != NULL; p = p->next) {
out << p->sta_name << "(" << p->dx << "," << p->dy << ")" << "->";
}
out << p->sta_name << "(" << p->dx << "," << p->dy << ")" << "。";
}
ostream &operator<<(ostream &out, const busChain &theChain) {
theChain.output(out);
return out;
}
#endif
#pragma once
3) busRoute.h
#ifndef busRoute_
#define busRoute_
#include "busChain.h"
#include <iostream>
using namespace std;
class busRoute {
public:
busRoute() {
num = 0;
sum = 0;
speed = 0;
price = 0;
time_ = 0;
}
~busRoute();
void initi1(int nn, int ss);//设置线路编号、站点总数
void initi2(double sp, double pp, double tt);//设置票价、发车间隔、速度
double getPrice() { return price; }
double getSpeed() { return speed; }
int getNum() { return num; }
int getSum() { return sum; }
double getTime_() { return time_; }
busChain& getLink() { return link; }
void output(ostream &out) {//输出本线路停经站点及其坐标
cout << num << ":";
cout << link;
link.get_distance();
cout << price << "元。" << time_ << "分钟。" << speed << "/分钟" << endl;
//发车时间间隔:速度:票价:
}
private:
int num;//线路编号
int sum;//站点总数
double speed;//速度
double price;//票价
double time_;//发车时间间隔
busChain link;//本公交线路链表
};
busRoute::~busRoute() {
num = 0;
sum = 0;
speed = 0;
price = 0;
time_ = 0;
link.~busChain();
}
void busRoute::initi1(int nn, int ss) {
num = nn;
sum = ss;
string str;
double xx, yy;
cout << "请依次输入各个站点的名称和坐标,均以空格分隔:" << endl;
for (int i = 1; i <= sum; i++) {
cout << "\t站点" << i << ":";
cin >> str >> xx >> yy;
link.insert(str, xx, yy);
}
}
//sp表示价格,pp表示发车间隔,tt表示速度
void busRoute::initi2(double sp, double pp, double tt) {
price = sp;
time_ = pp;
speed = tt;
}
ostream &operator<<(ostream &out, busRoute &theRoute) {
theRoute.output(out);
return out;
}
#endif
#pragma once
4) adjMatrix.h
#ifndef adjMatrix_
#define adjMatrix_
#include<iostream>
#include<stack>
#include<string.h>
#include "busRoute.h"
#define sn 30
//#define bn 100
#define noEdge 9999
using namespace std;
//两站点之间的线路信息
struct routeInfo {
int Info;//线路编号
double data;//数据
};
//两站点之间的线路信息,可有多条线路
struct ver {
routeInfo ro[sn];//每条线路信息
};
class adjMatrix {
public:
adjMatrix(int theCapacity);//构造函数
~adjMatrix();//析构函数
void insert(int i, int j, int nth, routeInfo rt);//插入元素
void erase(int i, int j, int nth);//删除元素
double getElement(int i, int j, int nth);//查找
int getM() const {//得到总边数
return m;
}
void ini();//初始化为无边
int search(int i, int j, int su);//查找站点i到站点j的所有线路中的最优解
int search2(int i, int j, int su, busRoute *bus);//查找站点i到站点j的所有线路中的最优解(判断是否加入等待时间)
void Dijkstra(double *dis, int *pre, int da, int su);//求da到其他点的最短路径
void outputPath(int pa, int pb, string *in, int *pre, int su);//输出pa到pb的最短线路
void Dijkstra2(double *dis, int *pre, int da, int su, busRoute *bus); //求da到其他点的最短路径,加入中间站点的等待时间
double find_(int Infomation, int su, busRoute *bus);//查找线路编号对应的等待时间
private:
ver * *theMatrix;//邻接矩阵
int n;//点数
int m;//边数
};
adjMatrix::adjMatrix(int theCapacity) {
n = theCapacity - 1;
theMatrix = new ver *[theCapacity];
for (int i = 0; i<theCapacity; i++) {
theMatrix[i] = new ver[theCapacity];
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
for (int k = 1; k <= sn; k++) {
theMatrix[i][j].ro[k].data = noEdge;
}
}
}
m = 0;
}
adjMatrix::~adjMatrix() {
for (int i = 0; i<n; i++) {
delete[] theMatrix[i];
}
delete[] theMatrix;
m = 0;//边数
n = 0;//点数
}
void adjMatrix::insert(int i, int j, int nth, routeInfo rt) {//在i-j处的第nth条线路信息中插入rt
if (theMatrix[i][j].ro[nth].data == noEdge) {
theMatrix[i][j].ro[nth] = rt;
m++;//边数
}
else {//已有边,更新信息
theMatrix[i][j].ro[nth] = rt;
}
}
void adjMatrix::erase(int i, int j, int nth) {//删除i-j处的第nth条线路的边
if (theMatrix[i][j].ro[nth].data == noEdge) {
cout << "Error!" << endl;
}
else {
theMatrix[i][j].ro[nth].data = noEdge;
m--;//边数
}
}
double adjMatrix::getElement(int i, int j, int nth) {//得到i-j处的第nth条线路的边值
return theMatrix[i][j].ro[nth].data;
}
void adjMatrix::ini() {//初始化均为无边,线路编号均为0
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
for (int k = 1; k <= sn; k++) {
theMatrix[i][j].ro[k].data = noEdge;
theMatrix[i][j].ro[k].Info = 0;
}
}
}
}
//求da到其他所有点的最短路径长度
void adjMatrix::Dijkstra(double *dis, int *pre, int da, int su) {
int *vis = new int[30];//标记数组
//memset(&vis, 0, 30);
for (int i = 1; i <= n; i++) {//初始化为未访问过
vis[i]=0;
}
for (int i = 1; i <= 26; i++) {
dis[i] = theMatrix[da][i].ro[search(da, i, su)].data;
//cout<<"测试:"<<this->search(da,i,su)<<" "<<dis[i]<<endl;
if (dis[i] == noEdge)
pre[i] = 0;
else
pre[i] = da;
}
dis[da] = 0;//da到da距离为0
/*for(int i=1;i<=8;i++) {
cout<<i<<" "<<dis[i]<<endl;
}
cout<<endl;*/
vis[da] = 1;//标记为访问过
for (int i = 1; i <= 25; i++) {
double temp = noEdge;
int v=0;
for (int j = 1; j <= 26; j++) {//找出距离 点集 最近的点
if (vis[j]==0 && dis[j]<temp) {
//cout << j << endl;
v = j;
temp = dis[j];
}
}
vis[v] = 1;
for (int j = 1; j <= 26; j++) {
if (vis[j] == 0 && theMatrix[v][j].ro[search(v, j, su)].data<noEdge) {//更新
double dd = dis[v] + theMatrix[v][j].ro[search(v, j, su)].data;
if (dd<dis[j]) {
dis[j] = dd;
pre[j] = v;
}
}
}
}
/*for (int i = 1; i <= 8; i++) {
cout << i << " " << dis[i] << endl;
}*/
}
//输出从pa到pb的最短路径
void adjMatrix::outputPath(int pa, int pb, string *in, int *pre, int su) {
if (pa == pb)
return;
int temp[sn];
int co = 1;//计数器
temp[co] = pb;//终点
co++;
int te = pre[pb];//pb的前驱
while (te != pa) {//将路径上的点存到temp数组中
temp[co] = te;
co++;
te = pre[te];
}
temp[co] = pa;
int info = theMatrix[temp[co]][temp[co - 1]].ro[search(temp[co], temp[co - 1], su)].Info;
cout << "线路" << info << ":";
for (int i = co; i >= 1; i--) {//倒序输出
if (i != 1) {
if (theMatrix[temp[i]][temp[i - 1]].ro[search(temp[i], temp[i - 1], su)].Info != info) {
//cout<<"测试:"<<in[temp[i]]<<" "<<in[temp[i-1]]<<endl;
cout << "站点" << in[temp[i]] << "。";
cout << "换乘线路" << theMatrix[temp[i]][temp[i - 1]].ro[search(temp[i], temp[i - 1], su)].Info << ":";
cout << "站点" << in[temp[i]] << ",";
}
else {
cout << "站点" << in[temp[i]] << ",";
}
info = theMatrix[temp[i]][temp[i - 1]].ro[search(temp[i], temp[i - 1], su)].Info;//更新当前线路信息
}
else {//最后一个站点
cout << "站点" << in[temp[i]] << "。" << endl;
}
}
}
//求da到其他所有点的最短路径长度,加入等待时间
void adjMatrix::Dijkstra2(double *dis, int *pre, int da, int su, busRoute *bus) {
int *vis = new int[30];//标记数组
//memset(&vis, 0, 30);
for (int i = 1; i <= n; i++) {//初始化为未访问过
vis[i] = 0;
}
for (int i = 1; i <= 26; i++) {
dis[i] = theMatrix[da][i].ro[search(da, i, su)].data;
//出发站点,不需要考虑等待时间
//cout<<"测试:"<<this->search(da,i,su)<<" "<<dis[i]<<endl;
if (dis[i] == noEdge)
pre[i] = 0;
else
pre[i] = da;
}
dis[da] = 0;//da到da距离为0
/*for(int i=1;i<=8;i++) {
cout<<i<<" "<<dis[i]<<endl;
}*/
cout << endl;
vis[da] = 1;//标记为访问过
int tt = 1;
for (int i = 1; i <= 26; i++) {
double temp = noEdge;
int v = 0;
for (int j = 1; j <= 26; j++) {//找出距离 点集 最近的点
if (vis[j] == 0 && dis[j]<temp) {
//cout << j << endl;
v = j;
temp = dis[j];
}
}
vis[v] = 1;
int oldInfo;
if (tt == 1) {
oldInfo = theMatrix[da][v].ro[search(da, v, su)].Info;//oldInfo初始赋值
}
tt = 2;
for (int j = 1; j <= 26; j++) {
if (oldInfo == theMatrix[v][j].ro[search2(v, j, su, bus)].Info) {//相同线路,不需加入等待时间
if (vis[j] == 0 && theMatrix[v][j].ro[search2(v, j, su, bus)].data < noEdge) {//更新
double dd = dis[v] + theMatrix[v][j].ro[search2(v, j, su, bus)].data;
//cout << "sss" << theMatrix[v][j].ro[search2(v, j, su, bus)].data << endl;
if (dd < dis[j]) {
dis[j] = dd;
pre[j] = v;
}
}
}
else {//不同线路,需要加入等待时间
if (vis[j] == 0 && theMatrix[v][j].ro[search2(v, j, su,bus)].data+find_(theMatrix[v][j].ro[search2(v, j, su, bus)].Info,su,bus)<noEdge) {//更新
double dd = dis[v] + theMatrix[v][j].ro[search2(v, j, su,bus)].data+find_(theMatrix[v][j].ro[search2(v, j, su, bus)].Info, su, bus);
//cout << "sss" << theMatrix[v][j].ro[search2(v, j, su, bus)].data << endl;
if (dd<dis[j]) {
dis[j] = dd;
pre[j] = v;
}
oldInfo = theMatrix[v][j].ro[search2(v, j, su, bus)].Info;//oldInfo更新
}
}
}
}
/*for (int i = 1; i <= 8; i++) {
cout << i << " " << dis[i] << endl;
}*/
}
//找出站点i到站点j的所有公交线路中的最优解 ,su表示两站点之间的线路条数
int adjMatrix::search(int i, int j, int su) {
double qq = theMatrix[i][j].ro[1].data;
int uu = 1;
for (int k = 2; k <= su; k++) {
if (theMatrix[i][j].ro[k].data<qq) {
qq = theMatrix[i][j].ro[k].data;
uu = k;
}
}
//cout<<"测试:"<<uu<<endl;
return uu;
}
//查找线路编号对应的等待时间
double adjMatrix::find_(int Information, int su, busRoute *bus) {
if (Information == 0)
return 0;
for (int i = 1; i<=su; i++) {
if (bus[i].getNum() == Information)
return bus[i].getTime_();
}
}
//找出站点i到站点j的所有公交线路中的最优解(加上等待时间) ,su表示两站点之间的线路条数
int adjMatrix::search2(int i, int j, int su,busRoute *bus) {
double qq = theMatrix[i][j].ro[1].data+find_(theMatrix[i][j].ro[1].Info,su,bus);
int uu = 1;
for (int k = 2; k <= su; k++) {
if (theMatrix[i][j].ro[k].data + find_(theMatrix[i][j].ro[k].Info, su, bus)<qq) {
qq = theMatrix[i][j].ro[k].data+find_(theMatrix[i][j].ro[k].Info,su,bus);
//cout << "ttt " << find_(theMatrix[i][j].ro[k].Info, su, bus) << endl;
uu = k;
}
}
//cout<<"测试:"<<uu<<endl;
return uu;
}
#endif
#pragma once