问题定义
本次课程设计要求协助中国大学生计算机设计大赛江苏省组委会,设计一款赛事管理系统,实现赛务相关的数据管理及信息服务,该系统能够为省级赛事管理解决以下问题:
(1)能够管理各参赛队的基本信息(包含参赛队编号,参赛作品名称,参赛学校,赛事类别,参赛者,指导老师),赛事类别共11项(参见大赛官网jsjds.blcu.edu.cn);包括增加、删除、修改参赛队伍的信息。
(2)从team.txt中读取参赛队伍的基本信息,实现基于二叉排序树的查找。根据提示输入参赛队编号,若查找成功,输出该赛事类别对应的基本信息(参赛作品名称、参赛学校、赛事类别、参赛者和指导老师信息),同时,输出查找成功时的平均查找长度ASL;否则,输出“查找失败!”。
(3)能够提供按参赛学校查询参赛团队(或根据赛事类别查询参赛团队),即,根据提示输入参赛学校名称(赛事类别),若查找成功,输出该学校参赛的(该赛事类别的)所有团队的基本信息,输出的参赛团队按赛事类别有序输出。(排序算法可从选择排序、插入排序、希尔排序、归并排序、堆排序中任意选择,并为选择算法的原因做出说明。)
(4)为省赛现场设计一个决赛叫号系统。所有参赛队按赛事组织文件中的赛事类别分到9个决赛室,决赛室按顺序叫号,被叫号参赛队进场,比赛结束后,下一参赛队才能进赛场。请模拟决赛叫号系统,演示省赛现场各决赛室的参赛队进场情况。(模拟时,要能直观展示叫号顺序与进场秩序一致)
(5)赛事系统为参赛者提供赛地的校园导游程序,为参赛者提供各种路径导航的查询服务。以我校长山校区提供比赛场地为例,(请为参赛者提供不少于10个目标地的导航。可为参赛者提供校园地图中任意目标地(建筑物)相关信息的查询;提供任意两个目标地(建筑物)的导航查询,即查询任意两个目的地(建筑物)之间的一条最短路径。
问题分析
赛事管理系统的实现需要涉及到多个方面,包括数据结构设计、搜索和排序算法的应用、用户交互和可视化等方面的问题。
在学习过程中,需要熟练掌握二叉排序树的基本原理和实现,选择合适的排序算法对查询结果进行排序,同时需要使用JavaFX等工具对系统界面进行设计。
这显然是一个图论问题,而且校园内道路一般是双向通行的,所以这是一个无向图。对 于图的存储结构而言,图中各个景点的存储结构有邻接表和邻接矩阵两种存储结构,考虑到 顶点个数少于 50 个,所以邻接表和邻接矩阵的复杂度相同。本题中选择使用邻接矩阵来表 示图。
任务中要求求解出图中景点的问路查询,即为给定两个源点,求解出两个顶点之间的最 短路径。根据数据结构课程所学知识,有多种经典算法可以解决最短路径问题,包括 Dijkstra 算法,Floyd-Warshell 算法,Bellman-Ford 算法和深度优先遍历。不同是算法有不同的算法复 杂度,考虑到校园中道路没有负权边,即算法均可解决最短路径问题。
其中 Dijkstra 算法求的是单源最短路径:即从一个结点出发到其它所有结点的最短路径, 算法的时间复杂度为 O(n 2 ),但题目要求任意两个结点的最短路径,所以还是要在外层增加 一个循环,以求得多源最短路径。
而 Floyd 算法求的是多源最短路径:即从任意结点出发到其它所有结点的最短路径,算 法的时间复杂度为 O(n 3 ),它可以一次性求得所有结点间的最短路径,且算法思想简单,便 于理解。所以我们这一项目采用 Floyd 算法来求解最短路径
系统主要包括以下几个组成部分:
-
数据结构设计:采用二叉排序树进行参赛队伍的基本信息存储,使用链式前向星存储导航图信息和路径信息;
-
界面设计:使用JavaFX等工具进行设计,包括各种输入框、下拉框,以及部分交互式地图展示等
-
二叉排序树实现参赛队伍的查找:使用二叉排序树实现快速查找,同时计算ASL;
-
排序算法实现学校(或赛事类别)查询:采用高效的排序算法对查询结果进行排序;
-
决赛叫号系统:采用队列模拟决赛叫号程序,实现按赛事类别叫号和模拟参赛队进场;
-
赛地校园导游:将校园地图导入系统,同时实现不少于10个目标地的导航。采用Floyd算法求解最短路径,同时提供地点信息和路径导航查询。
概要设计
#define MAXVEX 13 //最大顶点个数
#define INFINITY 3276//图的矩阵中A(i,i)记为0,若没有通路,记为INFINITY = 32762
int PathMatirx[MAXVEX][MAXVEX];
int ShortPath[MAXVEX][MAXVEX];
//结点的结构体--代表实际中的景点
typedef struct {
int Num;//景点的编号
string name; //校园景点名
string info; //校园景点的描述信息
}VextexType;
//邻接矩阵的数据类型
typedef struct {
int AdjMatrix[MAXVEX][MAXVEX]; //用二维数组来存放邻接矩阵
VextexType vex[MAXVEX]; //存放顶点信息
int vexnum; //顶点数
int arcnum; //边数
}MGraph;
void InitGraph();//初始化无向图
void Menu();//向用户显示菜单
void PrintAllInfo();//打印所有景点的信息
void MapDisplay();//显示地图函数
void ShortestPathOfAnyTwo(MGraph G);//查询任意两景点之间最短路径
void Floyd();//弗洛伊德算法求最短路径函数
void AddArc(MGraph G);//增加边<v,w>
DelVertex(MGraph G);//删去顶点v和所有与之关联的边
DelArc(MGraph G);//删去边<v,w>
UpdateIifo();//更新景点信息
void InitGraph(MGraph& G) {
int i = 0, j = 0;
G.vexnum = 13;
G.arcnum = 19;
for (int i = 0; i < 13; i++) {
G.vex[i].Num = i + 1;//第1号景点到第13号景点
}
void MapDisplay() {
system("mspaint Cloud_Picture.jpg");//使用Windows自带的画板打开校园云图
system("mspaint Plane_Figure.png");//使用Windows自带的画板打开校园平面图
}
Floyd算法(弗洛伊德算法)求最短路径:
两个准备的二维数组:
int PathMatirx[MAXVEX][MAXVEX];//记录对应点的最小路径的前驱点
int ShortPath[MAXVEX][MAXVEX];//记录顶点间的最小路径值
void Floyd(MGraph& G) {
//对Floyd的两个数组进行初始化
for (int i = 0; i < G.vexnum; i++) {
for (int j = 0; j < G.vexnum; j++) {
PathMatirx[i][j] = j;
ShortPath[i][j] = G.AdjMatrix[i][j];
}
}
for (int k = 0; k < G.vexnum; k++) {
for (int v = 0; v < G.vexnum; v++) {
for (int w = 0; w < G.vexnum; w++) {
if (ShortPath[v][w] > ShortPath[v][k] + ShortPath[k][w]) {
//更新最短路径
ShortPath[v][w] = ShortPath[v][k] + ShortPath[k][w];
//更新路径中间经过的节点
PathMatirx[v][w] = PathMatirx[v][k];
}
}
}
}
}
void UpdateIifo(MGraph& G) {
//更新景点信息
int number;
string info;
string name;
cout << "请输入您想更新的景点的编号" << endl;
cin >> number;
if (number<1 || number>G.vexnum) {
cout << "您的输入有误,请重新输入" << endl;
return;
}
cout << "请输入景点新的信息" << endl;
cin >> info;
cout << info << endl;
G.vex[number - 1].info = info;
cin >> name;
cout << name << endl;
G.vex[number - 1].name = name;
cout << "修改成功" << endl;
PrintAllInfo(G);
}
任意节点i到j的最短路径两种可能:
直接从i到j;
从i经过若干个节点k到j。
map(i,j)表示节点i到j最短路径的距离,对于每一个节点k,检查map(i,k)+map(k,j)小于map(i,j),如果成立,map(i,j) = map(i,k)+map(k,j);遍历每个k,每次更新的是除第k行和第k列的数。
prim算法求最小生成树
void Prim(MGraph& G) {
cout << "请输入您想从几号景点" << endl;
int choice;
cin >> choice;
struct {
int adjvex;
int lowcost;
}closedge[MAXVEX];
int i, e, k, m, min;
closedge[choice].lowcost = 0;
for (i = 0; i < G.vexnum; i++)
if (i != choice - 1)
{
closedge[i].adjvex = choice - 1;
closedge[i].lowcost = G.AdjMatrix[choice - 1][i];
}
for (e = 0; e < G.vexnum - 1; e++) {
min = INFINITY;
for (k = 0; k < G.vexnum; k++) {
if (closedge[k].lowcost != 0 && closedge[k].lowcost < min) {
m = k;
min = closedge[k].lowcost;
}
}
printf("从%s<------>%s:%dm\n", G.vex[closedge[m].adjvex].name, G.vex[m].name, closedge[m].lowcost);
closedge[m].lowcost = 0;
//当m加入后,更新closedge数组信息
for (i = 0; i < G.vexnum; i++) {
if (i != m && G.AdjMatrix[m][i] < closedge[i].lowcost) {
closedge[i].lowcost = G.AdjMatrix[m][i];
closedge[i].adjvex = m;
}
}
}
}