问题描述
本次课程设计要求协助中国大学生计算机设计大赛江苏省组委会,设计一款赛事管理系统,实现赛务相关的数据管理及信息服务,该系统能够为省级赛事管理解决以下问题:
(1)能够管理各参赛队的基本信息(包含参赛队编号,参赛作品名称,参赛学校,赛事类别,参赛者,指导老师),赛事类别共11项(参见大赛官网jsjds.blcu.edu.cn);包括增加、删除、修改参赛队伍的信息。
(2)从team.txt中读取参赛队伍的基本信息,实现基于二叉排序树的查找。根据提示输入参赛队编号,若查找成功,输出该赛事类别对应的基本信息(参赛作品名称、参赛学校、赛事类别、参赛者和指导老师信息),同时,输出查找成功时的平均查找长度ASL;否则,输出“查找失败!”。
(3)能够提供按参赛学校查询参赛团队(或根据赛事类别查询参赛团队),即,根据提示输入参赛学校名称(赛事类别),若查找成功,输出该学校参赛的(该赛事类别的)所有团队的基本信息,输出的参赛团队按赛事类别有序输出。(排序算法可从选择排序、插入排序、希尔排序、归并排序、堆排序中任意选择,并为选择算法的原因做出说明。)
(4)为省赛现场设计一个决赛叫号系统。所有参赛队按赛事组织文件中的赛事类别分到9个决赛室,决赛室按顺序叫号,被叫号参赛队进场,比赛结束后,下一参赛队才能进赛场。请模拟决赛叫号系统,演示省赛现场各决赛室的参赛队进场情况。(模拟时,要能直观展示叫号顺序与进场秩序一致)
(5)赛事系统为参赛者提供赛地的校园导游程序,为参赛者提供各种路径导航的查询服务。以我校长山校区提供比赛场地为例,(请为参赛者提供不少于10个目标地的导航。可为参赛者提供校园地图中任意目标地(建筑物)相关信息的查询;提供任意两个目标地(建筑物)的导航查询,即查询任意两个目的地(建筑物)之间的一条最短路径。
基本要求
(1)赛事数据要求存入文件(txt或excel)并能读入查询;
(2)赛地目的地查询,需提供目的地(建筑物)名称、代号、简介、两地之间路径长度等信息;
(3)输入数据形式和范围:赛事相关数据可从键盘输入,或自文件导入。
(4)界面要求:交互设计要合理,每个功能可以设计菜单,用户根据提示,完成相关功能的要求。
一、任务点1:参赛队伍管理
1、问题分析
能够管理各参赛队的基本信息(包含参赛队编号,参赛作品名称,参赛学校,赛事类别,参赛者,指导老师),赛事类别共11项(参见大赛官网jsjds.blcu.edu.cn);包括增加、删除、修改参赛队伍的信息。
2、算法设计
(1)首先,打印出赛事信息管理系统的菜单选项,包括增加新的参赛队伍信息、删除参赛队伍信息、修改参赛队伍信息以及退出管理信息系统选项。
(2)接下来,用户被要求输入要执行的操作编号。
(3)进入while循环,只要用户选择的操作编号不是0,就会一直执行循环内的代码。
如果用户选择的操作是1,表示要增加新的参赛队伍信息。在这种情况下,用户被要求输入新队伍的各项基本信息,并创建一个新的Team对象。然后,将该对象添加到teams列表中,并提示用户选择下一个操作。
如果用户选择的操作是2,表示要删除参赛队伍信息。在这种情况下,用户需要输入要删除的参赛队伍的编号。然后,遍历teams列表,查找与输入编号匹配的参赛队伍对象。如果找到匹配的参赛队伍,则将其从列表中删除,并显示删除成功的消息;否则,显示查找失败的消息。最后,提示用户选择下一个操作。
如果用户选择的操作是3,表示要修改参赛队伍信息。在这种情况下,用户需要输入要修改的参赛队伍的编号。然后,遍历teams列表,查找与输入编号匹配的参赛队伍对象。如果找到匹配的参赛队伍,则提示用户输入修改后的队伍基本信息,并更新该参赛队伍的属性值。最后,显示修改成功或查找失败的消息,并提示用户选择下一个操作。
如果用户输入的操作编号不是1、2、3或0,则显示输入有误的消息,并要求用户重新输入操作编号。
当用户输入的操作编号为0时,退出循环,结束程序的执行。
3、算法实现
设置一个Team类
public class Team {
int ID; //作品编号
String Name; //作品名称
String Category; //类别
String Student; //参赛者
String Teacher; //指导老师
String University; //大学
int Finalneedtime;
Team(){
Finalneedtime=(int)(Math.random()*10+5);
}
}
部分主要代码(完整代码见最后):
static void Operateteams(){
System.out.println("-----------------赛事信息管理系统--------------------");
System.out.println("1-增加新的参赛队伍信息");
System.out.println("2-删除参赛队伍信息");
System.out.println("3-修改参赛队伍信息");
System.out.println("0-退出管理信息系统");
System.out.println("--------------------------------------------------");
System.out.println("请输入你想进行的操作所对应的编号");
int p;
p=sc.nextInt();
while (p!=0){
if(p==1)
{
Team newteam=new Team();
System.out.println("请输入新添加的队伍基本信息");
System.out.print("请输入新的参赛队伍编号:");
newteam.ID=sc.nextInt();
System.out.print("请输入新队伍的参赛作品名称:");
newteam.Name=sc.next();
System.out.print("请输入新队伍的赛事类别:");
newteam.Category =sc.next();
System.out.print("请输入新队伍参赛者姓名:");
newteam.Student=sc.next();
System.out.print("请输入新队伍指导老师姓名:");
newteam.Teacher=sc.next();
System.out.print("请输入新队伍的学校:");
newteam.University=sc.next();
teams.add(newteam);
System.out.println("请选择选择下一个操作(输入0可以结束):");
p=sc.nextInt();
}
4、实验结果
增加队伍:
删除队伍:
修改队伍:
二、任务点2:基于二叉排序树的查找
从team.txt中读取参赛队伍的基本信息,实现基于二叉排序树的查找。根据提示输入参赛队编号,若查找成功,输出该赛事类别对应的基本信息(参赛作品名称、参赛学校、赛事类别、参赛者和指导老师信息),同时,输出查找成功时的平均查找长度ASL;否则,输出“查找失败!”。
1、问题分析
首先,需要将team.txt文件中的数据读取并构建二叉排序树(这里我选择读取excel表格中的数据进行处理),以便于后面的查询。从根节点开始,对于每个节点,如果查找关键字比节点关键字小,就进入左子树,否则进入右子树,直到找到查找关键字,或者找不到为止。
当查询到目标队伍时,输出该队伍的参赛作品名称、参赛学校、赛事类别、参赛者和指导老师信息。同时,通过公式计算出平均查找长度ASL,即从根节点到查找到的目标节点的平均路径长度。
2、算法设计
(1)首先,打印出文件录入查询系统的菜单选项,包括基于二叉排序树的根据赛队编号查询并输出信息,并求查找成功的平均查找长度ASL,以及退出选项。
(2)接下来,输出正在对文件中信息建立二叉排序树的提示信息。
声明变量p来接收用户输入的操作编号。
(3)使用循环,只要用户选择的操作编号不是0,就会一直执行循环内的代码。
如果用户选择的操作是1,表示根据赛队编号进行查询。用户被要求输入要查找的参赛队伍编号。接着,遍历teams列表,并将每个参赛队伍的信息插入到二叉排序树中。然后,执行查找功能以查找指定编号的参赛队伍。如果查找失败,则输出查找失败的消息;否则,输出查找成功的消息,并显示查找到的参赛队伍的详细信息。最后,计算文件信息建立的二叉排序树的平均成功查找长度ASL,并显示该值。
如果用户输入的操作编号不是1或0,则显示输入有误的消息,并要求用户重新输入操作编号。
当用户输入的操作编号为0时,退出循环,结束程序的执行。
3、算法实现
1、建立二叉树
for(int i=0;i< teams.size();i++){
Team t= (Team) teams.get(i);
tree.insert(t.ID,t.Name,t.Category,t.Student,t.Teacher,t.University);
}
2、ASL的计算(程序检查时ASL的计算有误,现修改如下)
public int findASL(int key) {
Node current = root;
while (current.data != key) {
ASL++;
if (key > current.data) {
current = current.right;
} else {
current = current.left;
}
if (current == null) {
return 0;
}
}
ASL++; //查程序时发现我寻找的是目标节点上一个节点的深度,在这儿进行ASL++的操作,便可以解决ASL计算错误的问题。
return ASL;
}
4、实验结果
代码修改前:
改正后:
三、任务点三:参赛团队查询
能够提供按参赛学校查询参赛团队(或根据赛事类别查询参赛团队),即,根据提示输入参赛学校名称(赛事类别),若查找成功,输出该学校参赛的(该赛事类别的)所有团队的基本信息,输出的参赛团队按赛事类别有序输出。(排序算法可从选择排序、插入排序、希尔排序、归并排序、堆排序中任意选择,并为选择算法的原因做出说明。)
1、问题分析
对于排序算法的选择,每个算法都有其自身的特点和适用场景,比如:选择排序在数据量比较少的情况下效率较高,但是在数据量大的情况下其时间复杂度较高;插入排序在数据量有序或近乎有序时效率较高;希尔排序可根据不同的增量序列来逐步缩小排序间隔,从而减少排序的比较次数和移动次数;归并排序使用分治法,将数据分成较小的部分逐步排序,然后将各个有序的部分合并起来,适用于大数据量和跨越式分布的数据;堆排序利用了二叉堆的特性,可以有效地实现排序。因此,你可以根据实际情况选择合适的排序算法,具体要选择哪个算法可以根据数据的实际情况和对速度、空间等方面的考虑进行选择。
对于查询结果的按照赛事类别进行排序,你可以使用排序算法来实现。具体来说,可以将团队信息存储在数组中,然后使用选择排序、插入排序、希尔排序、归并排序或堆排序等算法来对赛事类别进行排序,最终将排序后的结果输出即可。
2、算法设计
(1)首先,代码打印了赛事大类的列表,它包含了几个选项供用户选择操作。
(2)创建了一个名为"category"的ArrayList数组,长度为9,每个元素是一个ArrayList实例。这个数组用于将赛事按类别分组存储。
遍历名为"teams"的队伍列表中的每个队伍。
(3)根据队伍的"Category"属性将它们分别添加到"category"数组的不同位置。使用条件语句将每个队伍分组到特定的类别中。
用户被要求输入操作编号,并使用"sc.nextInt()"读取输入值。
进入while循环,只要用户输入的操作编号不为0,就会继续执行下面的代码。
(4)遍历"category"数组,通过对每个类别中的队伍列表调用“sort”方法来将队伍按照类别排序。
如果用户输入的操作编号是1,代码会要求用户输入学校名称,并在匹配到的队伍中输出队伍的信息,包括队伍编号、作品名称、学校、赛事类别、参赛者和指导教师。
在操作结束后,用户被要求选择下一个操作,如果已经尝试完毕,可以输入0来结束循环;否则,继续读取用户的输入。
如果用户输入的操作编号大于1或小于0,则输出错误信息,要求重新输入操作编号。
循环结束后,代码执行完毕。
3、算法实现
1、将赛事按类别分到不同的组别中(部分主要代码)
ArrayList catagory[];
catagory=new ArrayList[9];
for(int i=0;i<9;i++){
catagory[i]= new ArrayList();
}
for (int i=0;i<teams.size();i++){
Team t= (Team) teams.get(i);
if(Objects.equals(t.Category, "大数据实践")){
catagory[0].add(t);
continue;
}
if(Objects.equals(t.Category, "信息图形设计") || Objects.equals(t.Category, "动态信息影像(MG动画)") || Objects.equals(t.Category, "交互信息设计") || Objects.equals(t.Category, "数据可视化")){
catagory[1].add(t);
continue;
}
if(Objects.equals(t.Category, "人工智能实践赛")){
catagory[2].add(t);
continue;
}
if(Objects.equals(t.Category, "Web应用与开发") || Objects.equals(t.Category, "管理信息系统") || Objects.equals(t.Category, "算法设计与应用") || Objects.equals(t.Category, "移动应用开发(非游戏类)") || Objects.equals(t.Category, "移动应用开发")){
catagory[3].add(t);
continue;
}
if(Objects.equals(t.Category, "医药卫生") || Objects.equals(t.Category, "数字生活") || Objects.equals(t.Category, "运动健身") || Objects.equals(t.Category, "城市管理") || Objects.equals(t.Category, "行业应用")){
catagory[4].add(t);
continue;
}
if(Objects.equals(t.Category, "动画") || Objects.equals(t.Category, "纪录片") || Objects.equals(t.Category, "数字短片") || Objects.equals(t.Category, "微电影") || Objects.equals(t.Category, "新媒体漫画")){
catagory[5].add(t);
continue;
}
if(Objects.equals(t.Category, "产品设计") || Objects.equals(t.Category, "环境设计") || Objects.equals(t.Category, "平面设计")){
catagory[6].add(t);
continue;
}
if(Objects.equals(t.Category, "交互媒体设计") || Objects.equals(t.Category, "游戏设计") || Objects.equals(t.Category, "虚拟现实VR与增强现实AR" )){
catagory[7].add(t);
continue;
}
if(Objects.equals(t.Category, "汉语言文学") || Objects.equals(t.Category, "计算机基础与应用类课程微课") || Objects.equals(t.Category, "虚拟实验平台") || Objects.equals(t.Category, "中、小学数学或自然科学课程微课") || Objects.equals(t.Category, "计算机基础与应用类课程微课")){
catagory[8].add(t);
continue;
}
}
3、实验结果
四、任务点四:决赛叫号系统模拟
为省赛现场设计一个决赛叫号系统。所有参赛队按赛事组织文件中的赛事类别分到9个决赛室,决赛室按顺序叫号,被叫号参赛队进场,比赛结束后,下一参赛队才能进赛场。请模拟决赛叫号系统,演示省赛现场各决赛室的参赛队进场情况。(模拟时,要能直观展示叫号顺序与进场秩序一致)
1、问题分析
我们可以使用一个包含参赛队信息的列表来表示参赛队,然后按照赛事类别将他们分配到相应的决赛室。接着,我们可以使用一个队列来表示每个决赛室参赛队进场的顺序,决赛室依次从队头取出队首的参赛队进场,比赛结束后将该队的下一个队员加入队尾,这样便能够确保进场秩序和叫号顺序一致。
2、算法设计
(1)首先,定义了一个长度为9的ArrayList数组catagory和一个长度为9的Queue数组room,用于存储队伍信息和每个赛事对应的房间队列。
(2)使用for循环,将每个赛事按照类别分别存入不同的组别(ArrayList)和房间队列(PriorityQueue)。
(3)遍历所有的队伍信息,根据队伍的赛事类别将其放入对应的房间队列中。
在一个while循环中,只要还有队伍未被安排到房间中,就依次输出队伍信息。
输出队伍信息时,首先输出对应赛事大类的名称,然后输出房间号和参赛队伍编号。
从每个房间队列中取出一个队伍,并输出其赛事大类、房间号和队伍编号。
当所有房间队列都为空时,跳出循环。
最后输出一条信息表示叫号结束,并返回到程序的主目录。
2、算法实现
部分主要代码
static void Finalimitate(){
ArrayList catagory[];
catagory=new ArrayList[9];
Queue room[]=new Queue[9];
for(int i=0;i<9;i++){//将赛事按类别分到不同的组别中
catagory[i]= new ArrayList();
room[i]=new PriorityQueue<Team>(comID);
}//将不同的类别分组
for (int i=0;i<teams.size();i++){
Team t= (Team) teams.get(i);
if(Objects.equals(t.Category, "大数据实践")){
room[0].add(t);
continue;
}
if(Objects.equals(t.Category, "信息图形设计") || Objects.equals(t.Category, "动态信息影像(MG动画)") || Objects.equals(t.Category, "交互信息设计") || Objects.equals(t.Category, "数据可视化")){
room[1].add(t);
continue;
}
if(Objects.equals(t.Category, "人工智能实践赛")){
room[2].add(t);
continue;
}
……
while(!room[0].isEmpty()||!room[1].isEmpty()||!room[2].isEmpty()||!room[3].isEmpty()||!room[4].isEmpty()||!room[5].isEmpty()||!room[6].isEmpty()||!room[7].isEmpty()||!room[8].isEmpty()){
System.out.println("请以下队伍准备进入赛场");
System.out.println();
System.out.println("赛事大类 "+" 房间号 "+"参赛队编号");
for (int i=0;i<9;i++){
if(i==0&&!room[0].isEmpty())System.out.print("大数据应用 ");
if(i==1&&!room[1].isEmpty())System.out.print("信息可视化设计 ");
if(i==2&&!room[2].isEmpty())System.out.print("人工智能应用 ");
if(i==3&&!room[3].isEmpty())System.out.print("软件应用与开发 ");
if(i==4&&!room[4].isEmpty())System.out.print("物联网应用 ");
if(i==5&&!room[5].isEmpty())System.out.print("数媒动漫与短片 ");
if(i==6&&!room[6].isEmpty())System.out.print("数媒静态设计 ");
if(i==7&&!room[7].isEmpty())System.out.print("数媒游戏与交互设计 ");
if(i==8&&!room[8].isEmpty())System.out.print("微课与教学辅助 ");
Team t= (Team) room[i].poll();
if(t!=null)System.out.println(" "+(i+1)+" "+ t.ID);
}
}
System.out.println(" ");
System.out.println("叫号结束,返回目录");
}
3、实验结果
五、任务点五:基于校园地图的导航系统
赛事系统为参赛者提供赛地的校园导游程序,为参赛者提供各种路径导航的查询服务。以我校长山校区提供比赛场地为例,(请为参赛者提供不少于10个目标地的导航。可为参赛者提供校园地图中任意目标地(建筑物)相关信息的查询;提供任意两个目标地(建筑物)的导航查询,即查询任意两个目的地(建筑物)之间的一条最短路径。
1、数据结构的选择和概要设计
Floyd算法是一种图论算法,用于求解任意两点之间的最短路径。它的核心思想是动态规划,通过不断更新节点之间的距离,最终得到最短路径。本任务点我决定采用Floyd算法进行设计。
2、图例
3、算法设计
1、类
class Graph {
protected List<String> vertex;//存放顶点
protected int[][] edges;//存放边
protected boolean[] isVisited;//是否被访问
protected int numOfEdges;
int [][] accessible;
public Graph(int n) {
this.vertex = new ArrayList<>(n);
this.edges = new int[n][n];
this.isVisited = new boolean[n];
this.accessible=new int[10][10];
}
// 1. 获取节点个数
protected int getNumOfVertex();
// 2. 打印邻接矩阵
protected void printGraph();
// 3. 获取边的数目
protected int getNumOfEdges();
// 4. 获取某条边的权值
protected int getWeightOfEdges(int v1, int v2);
// 5. 添加节点
protected void addVertex(String v) ;
// 6. 添加边(双向)
protected void addEdge(int v1, int v2, int weight);
// 7.获取顶点索引对应的值
protected String getValueByIndex(int i);
}
4、算法实现
(1)floyd算法
static void floyd(int[][] graph,int len,String[] vax,int[][] path){
for (int k = 0; k < len; k++)
for (int i = 0; i < len; i++)
for (int j = 0; j < len; j++)
if (graph[i][j] > graph[i][k]+graph[k][j]){
graph[i][j] = graph[i][k] + graph[k][j];
//path记录路径
path[i][j]=path[i][k];
}
}
(2)打印最短路径
static void print_road(int[][] graph,int[][]path,String[] vax,int u,int v){
//打印出最短路径
int tmp=u;
Scanner sc = new Scanner(System.in);
int m = sc.nextInt();
System.out.print("从起点到终点的最短路径为:");
System.out.print(vax[m]+"-->");
while(!vax[tmp].equals(vax[v]))
{
System.out.print(vax[path[tmp][v]]);
tmp=path[tmp][v];
if(!vax[tmp].equals(vax[v]))
System.out.print("-->");
}
System.out.println();
System.out.println("最短路线长度是:"+graph[u][v]);
}
(3)创建无向图
static int[][] creat_Graph(int len,int[][] path){
//创建邻接矩阵
int m=99999;
int[][] graph=new int[len][len];
//初始化邻接矩阵
for(int i=0;i<graph.length;i++)
for(int j=0;j<graph[0].length;j++){
if(i==j)
graph[i][j]=0;
else
graph[i][j]=m;
//将path初始化为j
path[i][j]=j;
}
//根据图输入数据
//A的子路径
graph[0][1]=100;
graph[0][3]=200;
//B的子路径
graph[1][2]=80;
graph[1][3]=150;
//C的子路径
graph[2][4]=120;
graph[2][5]=110;
//D的子路径
graph[3][4]=50;
//E的子路径
graph[4][7]=150;
graph[4][8]=230;
//F的子路径
graph[5][6]=80;
graph[5][7]=60;
//G的子路径
graph[6][9]=100;
//H的子路径
graph[7][8]=90;
graph[7][9]=70;
//I的子路径
graph[8][9]=50;
return graph;
}