题目描述
今天铁路交通网络非常发达,人们在出差、旅游时,不仅关注交通费用,还关注里程和时间。请按照下图设计一个交通查询系统,能够满足旅客查询从任一个城市到另一个城市的最短里程、最低花费、最短时间、最少中转次数等问题。
[基本要求]
设计合适的数据结构和算法编写程序完成上述功能,并具有查询界面,能够按照下拉菜单选项进行选择查询。
思路
(1)这个交通查询系统实际上是带权无向图的应用。
(2)Flyod算法用于寻找图的最短路径。由于最短里程、最低花费、最短时间这三个本质上都是求最短路径,因此三者建立不同的最短路径长度矩阵和最短路径矩阵。
(3)广度遍历找到最少边数(中转次数最少)的路径。使用辅助队列,队列中最后一个城市的中转次数就是最少中转次数。广度优先遍历类似于层次遍历,当起点与终点不同层次时,使用一个变量记录遍历的层次数,找到终点时,层次数-1就是最少中转次数。
(4)界面设计,使用嵌套while循环,并且使用Sleep()函数来加强视觉效果。
代码
#include <iostream>
using namespace std;
//交通查询系统是个带权无向图
#define MaxInt 32767 //极大值
#define MVNum 100 //最大顶点数
//边信息
struct Info
{
int length;//城市距离
double time;//时长
int money;//花费
};
//图定义
struct AMGraph
{
int vexs[MVNum];//顶点表--用数字代表地名
Info arcs[8][8];//邻接矩阵---从1开始
int vexnum;//当前顶点数
int arcnum;//当前边数
};
//创建一个储存城市编号和中转次数的结构体
struct note {
int x; //城市编号1-7
int step; //中转次数
int pre; //前驱点在队列中的编号
};
//图操作算法
void InitG(AMGraph& G);//初始化图
void CreateG(AMGraph& G);//创建图
void Traverse(AMGraph& G);//遍历图
string OutCity(AMGraph& G, int x);//输入编号,输出城市名
//Floyd算法
void Flyod_Dis(AMGraph& G);//计算最短里程长度矩阵和最短里程长度的路径矩阵
void Flyod_Time(AMGraph& G);//计算最短时长矩阵和最短时长的路径矩阵
void Flyod_Money(AMGraph& G);//计算最少花费矩阵和最少花费的路径矩阵
void ShortDis(AMGraph& G, int x, int y);输出两地之间里程最短路径---输入两点编号
void ShortTime(AMGraph& G, int x, int y);//输出两地最短时长的路径
void ShortMoney(AMGraph& G, int x, int y);//输出两地最少花费的路径
//通过广度优先遍历得到最少中转次数
void Min_Cur(AMGraph& G, int start, int end);
//主界面
void MainSystem();
//主操作界面
void MainOperSystem();
//副操作界面
void SecondOperSystem();
//退出界面
void ExitSystem();
//初始化图
void InitG(AMGraph& G)
{
//初始化顶点表和邻接矩阵
for (int i = 1; i < 8; i++)
G.vexs[i] = i;//顶点表用地址编号
for (int i = 1; i < 8; i++)
for (int j = 1; j < 8; j++)
{
G.arcs[i][j] = { MaxInt,MaxInt,MaxInt };//将矩阵的三个变量全取MaxInt
}
}
void CreateG(AMGraph& G)
{
InitG(G);//初始化图
G.arcs[1][2] = { 2553,8,885 };//北京1->西安2
G.arcs[1][3] = { 695,2.3,202 };//北京1->郑州3
G.arcs[1][4] = { 704,2.5,225 };//北京1->徐州4
G.arcs[2][1] = { 2553,8,885 };//西安2->北京1
G.arcs[2][3] = { 511,1.5,148 };//西安2->郑州3
G.arcs[2][5] = { 812,3,283 };//西安2->成都5
G.arcs[3][1] = { 695,2.3,202 };//郑州3->北京1
G.arcs[3][2] = { 511,1.5,148 };//郑州3-》西安
G.arcs[3][4] = { 349,1.2,112 };//郑州3-》徐州4
G.arcs[3][6] = { 1579,5,495 };//郑州3-》广州6
G.arcs[4][1]= { 704,2.5,225 };//徐州4-》北京1
G.arcs[4][3] = { 349,1.2,112 };//徐州4-》郑州3
G.arcs[4][7] = { 651,2,162 };//徐州4-》上海7
G.arcs[5][2] = { 812,3,283 };//成都5-》西安2
G.arcs[5][6] = { 2358,7,684 };//成都5-》广州6
G.arcs[6][3] = { 1579,5,495 };//广州6-》郑州3
G.arcs[6][5] = { 2368,7,684 };//广州6-》成都5
G.arcs[6][7] = { 1385,4,386 };//广州6-》上海7
G.arcs[7][4] = { 651,2,162 };//上海7-》徐州4
G.arcs[7][6] = { 1385,4,386 };//上海7-》广州6
}
void Traverse(AMGraph& G)
{
for(int i=1;i<8;i++)
{
cout << G.vexs[i] << endl;
for (int j = 1; j < 8; j++)
{
if (G.arcs[i][j].length == MaxInt)//若与城市j无连接
continue;//跳过本次循环,直接下一次循环
cout <<"到"<<G.vexs[j]<<" : " << G.arcs[i][j].length << "公里 " << G.arcs[i][j].money
<<"元 " << G.arcs[i][j].time<<"h\n";
}
}
}
//输入编号,输出城市名
string OutCity(AMGraph& G, int x)
{
switch(x)
{
case 1:
return "北京";
case 2:
return "西安";
case 3:
return "郑州";
case 4:
return "徐州";
case 5:
return "成都";
case 6:
return "广州";
case 7:
return "上海";
default:
break;
}
}
//---Floyd算法---
//思路:
//(1)先将最短路径长度矩阵初始化为该图的邻接矩阵,同时最短路径矩阵初始化为直接路径
//(2)注意考察每个点,是否有非直接路径经过该点,且导致经过该点后路径长度变短。若有,则更新最短路径和最短路径长度
//(3)最后得到最短路径矩阵和最短路径长度矩阵
//在这里一条路有里程、花费、时间、最少中转次数,因此需要建立多种矩阵
//1.计算两地最短里程
int Dis[MVNum][MVNum];//最短里程长度矩阵
int Path_Dis[MVNum][MVNum];//最短里程长度的路径矩阵
//Flyod计算最短里程长度矩阵和最短里程长度的路径矩阵
void Flyod_Dis(AMGraph& G)
{
//初始化两个矩阵
for(int i=1;i<8;i++)
for (int j = 1; j < 8; j++)
{
Dis[i][j] = G.arcs[i][j].length;
if (i != j && G.arcs[i][j].length != MaxInt)
Path_Dis[i][j] = i;//顶点i到j之间有边
else
Path_Dis[i][j] = -1;//无边
}
for (int k = 1; k < 8; k++)//逐一考察每个顶点
{
for(int i=1;i<8;i++)
for(int j=1;j<8;j++)
if (Dis[i][j] > Dis[i][k] + Dis[k][j])//若经过该点导致路径长度变短
{
Dis[i][j] = Dis[i][k] + Dis[k][j];//修改最短路径长度
Path_Dis[i][j] = Path_Dis[k][j];//修改最短路径
}
}
}
//输出两地之间里程最短路径---输入两点编号
void ShortDis(AMGraph& G, int x, int y)
{
int apath[MVNum] = { 0 }, d = 0;//存放一条最短路径的中间顶点(反向)及其顶点个数
if (Dis[x][y] != MaxInt && x != y)//若顶点x和y之间存在路径
{
cout << "\n从" << OutCity(G, x) << "到" << OutCity(G, y) << "的最短里程路径为: ";
int k = Path_Dis[x][y];
apath[d] = y;//路径上添加终点
while (k != x)//循环添加中间点
{
d++;
apath[d] = k;
k = Path_Dis[x][k];
}
d++;
apath[d] = x;//添加起点
cout << OutCity(G, apath[d]);//输出起点
for (int s = d - 1; s >= 0; s--)
cout << "-->" << OutCity(G,apath[s]);
cout << "\n\n路径最短里程:" << Dis[x][y] << endl << endl;
}
}
//2.计算两地最短时长
double Time[MVNum][MVNum];//最短时长矩阵
int Path_Time[MVNum][MVNum];//最短时长的路径矩阵
//计算最短时长矩阵和最短时长的路径矩阵
void Flyod_Time(AMGraph& G)
{
//初始化两个矩阵
for (int i = 1; i < 8; i++)
for (int j = 1; j < 8; j++)
{
Time[i][j] = G.arcs[i][j].time;
if (i != j && G.arcs[i][j].time != MaxInt)
Path_Time[i][j] = i;//顶点i到j之间有边
else
Path_Time[i][j] = -1;//无边
}
for (int k = 1; k < 8; k++)//逐一考察每个顶点
{
for (int i = 1; i < 8; i++)
for (int j = 1; j < 8; j++)
if (Time[i][j] > Time[i][k] + Time[k][j])//若经过该点导致路径长度变短
{
Time[i][j] = Time[i][k] + Time[k][j];//修改最短路径长度
Path_Time[i][j] = Path_Time[k][j];//修改最短路径
}
}
}
//输出两地最短时长的路径
void ShortTime(AMGraph& G, int x, int y)
{
int apath[MVNum] = { 0 }, d = 0;//存放一条最短路径的中间顶点(反向)及其顶点个数
if (Time[x][y] != MaxInt && x != y)//若顶点x和y之间存在路径
{
cout << "\n从" << OutCity(G, x) << "到" << OutCity(G, y) << "的最短时长路径为: ";
int k = Path_Time[x][y];
apath[d] = y;//路径上添加终点
while (k != x)//循环添加中间点
{
d++;
apath[d] = k;
k = Path_Time[x][k];
}
d++;
apath[d] = x;//添加起点
cout << OutCity(G, apath[d]);//输出起点
for (int s = d - 1; s >= 0; s--)
cout << "-->" << OutCity(G, apath[s]);
cout << "\n\n路径最短时长:" << Time[x][y] << endl << endl;
}
}
//3.计算两地最少花费
int Money[MVNum][MVNum];//最少花费矩阵
int Path_Money[MVNum][MVNum];// 最少花费的路径矩阵
//计算最少花费矩阵和最少花费的路径矩阵
void Flyod_Money(AMGraph& G)
{
//初始化两个矩阵
for (int i = 1; i < 8; i++)
for (int j = 1; j < 8; j++)
{
Money[i][j] = G.arcs[i][j].money;
if (i != j && G.arcs[i][j].money != MaxInt)
Path_Money[i][j] = i;//顶点i到j之间有边
else
Path_Money[i][j] = -1;//无边
}
for (int k = 1; k < 8; k++)//逐一考察每个顶点
{
for (int i = 1; i < 8; i++)
for (int j = 1; j < 8; j++)
if (Money[i][j] > Money[i][k] + Money[k][j])//若经过该点导致路径长度变短
{
Money[i][j] = Money[i][k] + Money[k][j];//修改最短路径长度
Path_Money[i][j] = Path_Money[k][j];//修改最短路径
}
}
}
//输出两地最少花费的路径
void ShortMoney(AMGraph& G, int x, int y)
{
int apath[MVNum] = { 0 }, d = 0;//存放一条最短路径的中间顶点(反向)及其顶点个数
if (Money[x][y] != MaxInt && x != y)//若顶点x和y之间存在路径
{
cout << "\n从" << OutCity(G, x) << "到" << OutCity(G, y) << "的最少花费路径为: ";
int k = Path_Money[x][y];
apath[d] = y;//路径上添加终点
while (k != x)//循环添加中间点
{
d++;
apath[d] = k;
k = Path_Money[x][k];
}
d++;
apath[d] = x;//添加起点
cout << OutCity(G, apath[d]);//输出起点
for (int s = d - 1; s >= 0; s--)
cout << "-->" << OutCity(G, apath[s]);
cout << "\n\n路径最少花费:" << Money[x][y] << endl<<endl;
}
}
//通过广度优先遍历得到最少中转次数------循环每一层,若找到终点则停止
void Min_Cur(AMGraph& G, int start, int end)
{
if (start == end) {
cout << "\n不需中转" << endl;
return;
}
note* que = new note[10]; //简易队列,用于进行广度优先遍历
int book[10] = { 0 }; //标记数组
int head = 0, tail = 0; //队头队尾初始化
int cur = 0; //临时变量
int flag = 0; //是否到达目标城市
//从start号城市出发,将start加入队列
que[tail].x = start;
que[tail].step = 0; //步数为0
que[tail].pre = -1; //队列头的前驱顶点为-1
tail++;
book[start] = 1; //标记start号城市已存在队列中
//当队列不为空时进行循环
while (head < tail) {
cur = que[head].x; //当前队列中队首的城市编号
for (int i = 1; i < 8; i++) {
//从城市cur到城市i是否有班次并且判断i是否已经在队列中
if ( G.arcs[cur][i].length!= MaxInt && book[i] == 0) {
que[tail].x = i;//入队
que[tail].step = que[head].step + 1; //中转次数+1,步数是父亲步数+1
que[tail].pre = head; //由哪个点扩展出来的,谁就是它的父顶点
tail++;//队尾指针后移
book[i] = 1;//标记
}
//如果到达目标城市,停止扩展,退出循环
if (que[tail - 1].x == end) {
flag = 1;
break;
}
}
if (flag) {
break;
}
head++; //队首出队
}
//队列中最后一个城市的中转次数就是最少中转次数
cout << endl<<OutCity(G,start) << "-->" << OutCity(G, end) << "的最少中转次数为:" << que[tail - 1].step - 1 << "次" << endl;
int rear = tail - 1;
cout << "\n中转路线可以为: " ;
int* city = new int[8];
int num = 0;
for (int i = 0; i < 8; i++)
city[i] = 0;
while (true) {
//如果没有达到队首,即起点城市
if (que[rear].pre != -1)
{
city[num] = que[rear].x;
num++;
}
else {
city[num] = que[rear].x;
break;
}
rear = que[rear].pre;
}
cout << OutCity(G, city[num]);
do {
num--;
cout << "-->" << OutCity(G, city[num]);
} while (city[num] != end);
cout << endl << endl;
delete[]que;
delete[]city;
}
#include <windows.h>
//主界面
void MainSystem()
{
cout << " ************************************************************************\n";
cout << " *******||--------------------------------------------------||*******\n";
cout << " *****||--------------**********************--------------||*****\n";
cout << " ***||--------------*欢迎使用交通查询系统*--------------||***\n";
cout << " *****||--------------**********************--------------||*****\n";
cout << " *******||--------------------------------------------------||*******\n";
cout << " *********||**************************************************||*********\n";
cout << " || Loading:";
for (int i = 0; i < 6; i++)
{
Sleep(500);
cout << " 。 ";
}
cout << " ||\n" << " || ||\n";
}
//主操作界面
void MainOperSystem()
{
cout << " ************************************************************************\n";
cout << " *******||--------------------------------------------------||*******\n";
cout << " *****||-----****************************************-----||*****\n";
cout << " ***||-----*请输入你需要查询的起点和终点的城市序号*-----||***\n";
cout << " *****||-----****************************************-----||*****\n";
cout << " *******||--------------------------------------------------||*******\n";
cout << " *********||**************************************************||*********\n";
cout << " || 1. 北京 ||\n";
cout << " || 2. 西安 ||\n";
cout << " || 3. 郑州 ||\n";
cout << " || 4. 徐州 ||\n";
cout << " || 5. 成都 ||\n";
cout << " || 6. 广州 ||\n";
cout << " || 7. 上海 ||\n";
cout << " || 0. 退出 ||\n";
cout << " || ||\n";
}
//副操作界面
void SecondOperSystem()
{
cout << " ************************************************************************\n";
cout << " *******||--------------------------------------------------||*******\n";
cout << " *****||-----------****************************-----------||*****\n";
cout << " ***||-----------*请输入你需要进行的查询操作*-----------||***\n";
cout << " *****||-----------****************************-----------||*****\n";
cout << " *******||--------------------------------------------------||*******\n";
cout << " *********||**************************************************||*********\n";
cout << " || 1. 最短里程 ||\n";
cout << " || 2. 最低花费 ||\n";
cout << " || 3. 最短时间 ||\n";
cout << " || 4. 最少中转次数 ||\n";
cout << " || 0. 退出 ||\n";
cout << " || ||\n";
}
//退出界面
void ExitSystem()
{
cout << " ************************************************************************\n";
cout << " *******||--------------------------------------------------||*******\n";
cout << " *****||-----------****************************-----------||*****\n";
cout << " ***||-----------*******感谢您的使用!*******-----------||***\n";
cout << " *****||-----------****************************-----------||*****\n";
cout << " *******||--------------------------------------------------||*******\n";
cout << " *********||**************************************************||*********\n";
cout << " ||¥-¥-¥-¥-¥-¥-¥-¥-¥-¥-¥-¥-¥-¥-¥-¥-¥||\n";
}
int main()
{
AMGraph G;
InitG(G);
CreateG(G);
int city1,city2;//输入城市的选择
int choice;//查询操作
MainSystem();
while (true)
{
system("cls");
MainOperSystem();; //主操作界面显示
cin >> city1 >> city2;
if (city1 == 0 && city2 == 0)
{
system("cls");
ExitSystem();
Sleep(1000);
break;
}
if (city1 > 0 && city1 < 8 && city2>0 && city2 < 8&&city1!=city2)
{
system("cls");//清空界面
while (true)
{
SecondOperSystem();
cin >> choice;
if (choice == 0) //退出副界面
break;
switch (choice)
{
case 1: //最短里程
Flyod_Dis(G);
ShortDis(G, city1, city2);//输出最短里程路径
system("pause");
system("cls");
break;
case 2: //最低花费
Flyod_Money(G);
ShortMoney(G, city1, city2);
system("pause");
system("cls");
break;
case 3: //最短时间
Flyod_Time(G);
ShortTime(G, city1, city2);
system("pause");
system("cls");
break;
case 4: //
Min_Cur(G, city1, city2);
system("pause");
system("cls");
break;
default:
cout << "输入错误!" << endl;
system("pause");
system("cls");
break;
}
}
}
else {
cout << "输入有误,请重新输入!\n";
system("pause");
}
}
}
运行结果
初始界面
主操作界面
副操作界面
退出界面