- 程序设计题目
交通查询系统设计(难度系数 1.5)
[问题描述]
今天铁路交通网络非常发达,人们在出差、旅游时,不仅关注交通费用,还关注里程和时间。请按照下图设计一个交通查询系统,能够满足旅客查询从任一个城市到另一个城市的最短里程、最低花费、最短时间、最少中转次数等问题。
[基本要求]
设计合适的数据结构和算法编写程序完成上述功能,并具有查询界面,能够按照下拉菜单选项进行选择查询。
- 算法所需的数据结构
- 邻接矩阵
- 弗洛伊德算法
Floyd算法是一个经典的动态规划算法。用通俗的语言来描述的话,首先我们的目标是寻找从点i到点j的最短路径。从动态规划的角度看问题,我们需要为这个目标重新做一个诠释:
从任意节点i到任意节点j的最短路径不外乎2种可能,1是直接从i到j,2是从i经过若干个节点k到j。所以,我们假设Dis(i,j)为节点u到节点v的最短路径的距离,对于每一个节点k,我们检查Dis(i,k) + Dis(k,j) < Dis(i,j)是否成立,如果成立,证明从i到k再到j的路径比i直接到j的路径短,我们便设置Dis(i,j) = Dis(i,k) + Dis(k,j),这样一来,当我们遍历完所有节点k,Dis(i,j)中记录的便是i到j的最短路径的距离。
- 算法设计思想(流程图或者思维导图,并标注各模块对应的函数说明)
- 代码实现
编辑对话框、定义变量及为函数添加处理程序
pch.h
// pch.h: 这是预编译标头文件。
// 下方列出的文件仅编译一次,提高了将来生成的生成性能。
// 这还将影响 IntelliSense 性能,包括代码完成和许多代码浏览功能。
// 但是,如果此处列出的文件中的任何一个在生成之间有更新,它们全部都将被重新编译。
// 请勿在此处添加要频繁更新的文件,这将使得性能优势无效。
#ifndef PCH_H
#define PCH_H
// 添加要在此处预编译的标头
#include "framework.h"
#endif //PCH_H
#include<iostream>
#include<iomanip>
using namespace std;
#define MaxInt 32767
#define MaxNum 20
typedef struct Data
{
int d;
int c;
double t;
int ch;
};
typedef struct
{
int vexs[MaxNum];//顶点表
Data arcs[7][7];//邻接矩阵
int vexnum, arcnum;//图的当前点数和边数
}myGraph;
void Init(myGraph& G);
void create_dis(myGraph& G);
void init(bool**& S, double**& D, int**& Path);
void Short_Floyddis(myGraph& G, bool**& S, double**& D, int**& Path);
void Short_Floydcost(myGraph& G, bool**& S, double**& D, int**& Path);
void Short_Floydtime(myGraph& G, bool**& S, double**& D, int**& Path);
void Short_Floydchange(myGraph& G, bool**& S, double**& D, int**& Path);
int shortDistance(double** D, int i, int j);
int shortCost(double** D, int i, int j);
double shortTime(double** D, int i, int j);
int shortChange(double** D, int i, int j);
pch.cpp
// pch.cpp: 与预编译标头对应的源文件
#include "pch.h"
// 当使用预编译的头时,需要使用此源文件,编译才能成功。
//初始化邻接矩阵
void Init(myGraph& G)
{
G.vexnum = 7; G.arcnum = 10;
for (int i = 1; i <= G.vexnum; ++i)
G.vexs[i - 1] = i;
for (int i = 0; i < G.vexnum; ++i)
for (int j = 0; j < G.vexnum; ++j)
{
G.arcs[i][j] = { MaxInt,MaxInt,MaxInt,MaxInt };
}
}
//邻接矩阵
void create_dis(myGraph& G)
{
Init(G);
G.arcs[0][1] = { 2553, 885,8.0,1 };
G.arcs[0][2] = { 695,202,2.3,1 };
G.arcs[0][3] = { 704,225,2.5,1 };
G.arcs[1][2] = { 511,148,1.5,1 };
G.arcs[1][4] = { 812,283,3.0,1 };
G.arcs[2][3] = { 349,112,1.2,1 };
G.arcs[2][5] = { 1579,495,5.0,1 };
G.arcs[3][6] = { 651,162,2.0,1 };
G.arcs[4][5] = { 2368,684,7.0,1 };
G.arcs[5][6] = { 1385,386,4.0,1 };
for (int i = 0; i < G.vexnum; ++i)
for (int j = 0; j < G.vexnum; ++j)
G.arcs[j][i] = G.arcs[i][j];
}
//使用一维数组模拟二维数组
void init(bool**& S, double**& D, int**& Path)
{
S = new bool* [MaxNum];
for (int i = 0; i < MaxNum; ++i)
{
S[i] = new bool[MaxNum];
}
D = new double* [MaxNum];
for (int i = 0; i < MaxNum; ++i)
{
D[i] = new double[MaxNum];
}
Path = new int* [MaxNum];
for (int i = 0; i < MaxNum; ++i)
{
Path[i] = new int[MaxNum];
}
}
//利用弗洛伊德(Floyd)算法求上图中城市间最短路径
void Short_Floyddis(myGraph& G, bool**& S, double**& D, int**& Path)
{
init(S, D, Path);//二维数组
int n = G.vexnum;
for (int i = 0; i < n; ++i)
for (int j = 0; j < n; ++j)
{
D[i][j] = G.arcs[i][j].d;
if (D[i][j] < MaxInt && i != j)Path[i][j] = i;//如果i和j之间有弧,则将j的前驱置为i,否则置为-1
else Path[i][j] = -1;
}
for (int k = 0; k < n; ++k)
for (int i = 0; i < n; ++i)
for (int j = 0; j < n; ++j)
if (D[i][k] + D[k][j] < D[i][j])//从i到k到j的一条路径更短
{
D[i][j] = D[i][k] + D[k][j];//更新D[i][j]
Path[i][j] = Path[k][j];//将j的前驱置为k
}
}
void Short_Floydcost(myGraph& G, bool**& S, double**& D, int**& Path)
{
init(S, D, Path);//二维数组
int n = G.vexnum;
for (int i = 0; i < n; ++i)
for (int j = 0; j < n; ++j)
{
D[i][j] = G.arcs[i][j].c;
if (D[i][j] < MaxInt && i != j)Path[i][j] = i;//如果i和j之间有弧,则将j的前驱置为i,否则置为-1
else Path[i][j] = -1;
}
for (int k = 0; k < n; ++k)
for (int i = 0; i < n; ++i)
for (int j = 0; j < n; ++j)
if (D[i][k] + D[k][j] < D[i][j])//从i到k到j的一条路径更短
{
D[i][j] = D[i][k] + D[k][j];//更新D[i][j]
Path[i][j] = Path[k][j];//将j的前驱置为k
}
}
void Short_Floydtime(myGraph& G, bool**& S, double**& D, int**& Path)
{
init(S, D, Path);//二维数组
int n = G.vexnum;
for (int i = 0; i < n; ++i)
for (int j = 0; j < n; ++j)
{
D[i][j] = G.arcs[i][j].t;
if (D[i][j] < MaxInt && i != j)Path[i][j] = i;//如果i和j之间有弧,则将j的前驱置为i,否则置为-1
else Path[i][j] = -1;
}
for (int k = 0; k < n; ++k)
for (int i = 0; i < n; ++i)
for (int j = 0; j < n; ++j)
if (D[i][k] + D[k][j] < D[i][j])//从i到k到j的一条路径更短
{
D[i][j] = D[i][k] + D[k][j];//更新D[i][j]
Path[i][j] = Path[k][j];//将j的前驱置为k
}
}
void Short_Floydchange(myGraph& G, bool**& S, double**& D, int**& Path)
{
init(S, D, Path);//二维数组
int n = G.vexnum;
for (int i = 0; i < n; ++i)
for (int j = 0; j < n; ++j)
{
D[i][j] = G.arcs[i][j].ch;
if (D[i][j] < MaxInt && i != j)Path[i][j] = i;//如果i和j之间有弧,则将j的前驱置为i,否则置为-1
else Path[i][j] = -1;
}
for (int k = 0; k < n; ++k)
for (int i = 0; i < n; ++i)
for (int j = 0; j < n; ++j)
if (D[i][k] + D[k][j] < D[i][j])//从i到k到j的一条路径更短
{
D[i][j] = D[i][k] + D[k][j];//更新D[i][j]
Path[i][j] = Path[k][j];//将j的前驱置为k
}
}
//最短里程
int shortDistance(double** D, int i, int j)
{
i--; j--;
return D[i][j];
}
//最低花费
int shortCost(double** D, int i, int j)
{
i--; j--;
return D[i][j];
}
//最短时间
double shortTime(double** D, int i, int j)
{
i--; j--;
return D[i][j];
}
//最少中转次数
int shortChange(double** D, int i, int j)
{
i--; j--;
return D[i][j] - 1;
}
inquireDlg.cpp
void CinquireDlg::OnClickedButtonQuire()
{
// TODO: 在此添加控件通知处理程序代码
myGraph G;
create_dis(G);
UpdateData(TRUE);//刷新数据,如果UpdateData(TRUE) == 将控件的值赋值给成员变量;UpdateData(FALSE) == 将成员变量的值赋值给控件。
m_start;
m_end;
UpdateData(FALSE);
bool** Sd, ** Sc, ** St, ** Sch; int** Pathd, ** Pathc, ** Patht, ** Pathch;
double** Dd, ** Dc, ** Dt, ** Dch;
Short_Floyddis(G, Sd, Dd, Pathd);
Short_Floydcost(G, Sc, Dc, Pathc);
Short_Floydtime(G, St, Dt, Patht);
Short_Floydchange(G, Sch, Dch, Pathch);
UpdateData(TRUE);
m_dis = shortDistance(Dd, m_start+1, m_end+1);
m_cost = shortCost(Dc, m_start+1, m_end+1);
m_time = shortTime(Dt, m_start+1, m_end+1);
m_change = shortChange(Dch, m_start+1, m_end+1);
UpdateData(FALSE);
}
void CinquireDlg::OnBnClickedOk()
{
// TODO: 在此添加控件通知处理程序代码
CDialogEx::OnOK();
}
- 测试数据与结果
1.测试数据:
里程邻接矩阵:
价格邻接矩阵:
时间邻接矩阵:
中转次数邻接矩阵:
- 测试结果
- 时间复杂度与空间复杂度
时间复杂度:复杂度最高的是三次for循环中的if语句,时间复杂度为O(n^3).
空间复杂度:空间占用较大的是二维数组,空间复杂度为O(n^2).
- 结束语(逐条给出课程设计过程中所遇到的主要困难及解决方案,不要写低级错误,不要人为制造错误)
1、一开始我是用迪杰斯特拉算法(作为主要算法)来实现的,但是我发现在进行查询的时候,每进行一次查询,都要调用迪杰斯特拉算法函数然后再进行结果输出,这样比较麻烦。后来我改用了弗洛伊德算法(更加简洁),直接运行算法函数生成最低权值和的二维数组,查询时直接调用输出函数(通过二维数组)得到结果。
2、我还试图用四个邻接矩阵来实现功能,后来采用结构体数组方法只使用一个邻接矩阵来进行操作.
3、一开始对MFC的控件使用不熟悉,将combox类型定义为int后其无法使用AddString函数输入数据,而应在属性处输入数据,并用分号隔开。