目录
中国大学生计算机设计大赛是我国高校面向本科生的计算机应用设计大赛,大赛旨在激发学生学习计算机知识和技能的兴趣与潜能,提高学生运用信息技术解决实际问题的综合能力。通过大赛这种计算机教学实践形式,可展示师生的教与学成果,最终以赛促学,以赛促教,以赛促创。该赛事在历届学生中影响力较大,参与者众多,请结合2021届省赛参赛的数据,借助数据结构课程所学的相关知识,通过对数据的处理和分析,全面了解赛事组织及管理的体系,以及数据结构设计及数据处理在信息管理系统中应用的重要性。赛事相关数据存储在文本文件和excel文件中,相应的文件信息说明如表1所示。其中,各个文件中不同的数据项之间均使用#分隔,图1中给出了文件team.txt中参赛信息的对应数据示例。
图1. 参赛队基本信息
【问题描述】
要求协助中国大学生计算机设计大赛江苏省组委会,设计一款赛事管理系统,实现赛务相关的数据管理及信息服务,该系统能够为省级赛事管理解决以下问题:
(1)从team.txt中读取参赛队伍的基本信息,能够管理各参赛队的基本信息(包含参赛队编号,参赛作品名称,参赛学校,赛事类别,参赛者,指导老师),赛事类别共11项(参见大赛官网jsjds.blcu.edu.cn);包括增加、删除、修改参赛队伍的信息。
(2)实现基于二叉排序树的查找。根据提示输入参赛队编号,若查找成功,输出该赛事类别对应的基本信息(参赛作品名称、参赛学校、赛事类别、参赛者和指导老师信息),同时,输出查找成功时的平均查找长度ASL;否则,输出“查找失败!”。请输出ASL的计算表达式和结果值。
(3)能够提供按参赛学校查询参赛团队,根据提示输入参赛学校名称,若查找成功,输出该学校参赛的所有团队的基本信息,输出的参赛团队需有序输出(按参赛队编号)。(排序算法可从选择排序、插入排序、希尔排序、归并排序、堆排序中任意选择,并为选择算法的原因做出说明。)
(4)为省赛现场设计一个决赛叫号系统。所有参赛队按赛事组织文件中的赛事类别分到9个决赛室,决赛室按顺序叫号,被叫号参赛队进场,比赛结束后,下一参赛队才能进赛场。请模拟决赛叫号系统,演示省赛现场各决赛室的参赛队进场情况。(模拟时,各参赛队进场比赛时间可设为0.5秒)
(5)赛事系统为参赛者提供赛地的校园导游程序。为参赛者提供各种路径导航的查询服务。以我校长山校区提供比赛场地为例,(请为参赛者提供不少于10个目标地的导航。可为参赛者提供校园地图中任意目标地(建筑物)相关信息的查询;提供图中任意目标地(建筑物)的问路查询,即查询任意两个目的地(建筑物)之间的一条最短的简单路径。
从team.txt中读取参赛队伍的基本信息,能够管理各参赛队的基本信息(包含参赛队编号,参赛作品名称,参赛学校,赛事类别,参赛者,指导老师),赛事类别共11项(参见大赛官网jsjds.blcu.edu.cn);包括增加、删除、修改参赛队伍的信息。
这个任务涉及到了数据的输入、处理和管理,具体可以被分解为以下步骤:
1. **读取数据**:从一个名为"team.txt"的文件中读取数据。这个文件包含了各个参赛队的基本信息,包括参赛队编号,参赛作品名称,参赛学校,赛事类别,参赛者,指导老师。首先,需要编写一个能够处理这个文件的程序,并把信息读取到适当的数据结构中。
2. **数据管理**:一旦信息被读入,就需要进行数据管理。这可能涉及到一个数据结构(例如,一个类或者一个字典),来存储和检索这些信息。数据管理应该包括增加新的队伍,删除已有的队伍,以及修改队伍信息的能力。
3. **赛事类别**:赛事类别有不同的项,这可能意味着需要一个列表或者一个数组来存储这些类别,并且可能需要对应的功能来处理与这些类别有关的信息。
4. **界面**:如果这是一个用户界面,你可能还需要一个方法来与用户交互。这可以是一个命令行界面,也可以是一个图形用户界面,取决于需求和资源。
对于实现这个任务,可能会用到如下技术:
- **文件 I/O**:这是处理文件,例如读取和写入的技术。
- **数据结构**:例如列表、字典、类等,用来存储和管理数据。
- **条件语句**:例如if-else语句,用来决定何时执行何种操作(例如增加、删除或修改队伍信息)。
- **循环**:例如for或while循环,用来遍历数据结构。
- **函数**:定义和调用函数,用来执行特定任务,例如添加新队伍或删除队伍。
Class Team来用来保存每个参赛队的信息
里面有id, name, school, competition_type, participants, mentor信息.
class Team:
def __init__(self, id, name, school, competition_type, participants, mentor):
self.id = id
self.name = name
self.school = school
self.competition_type = competition_type
self.participants = participants
self.mentor = mentor
下面设置一个类CompetitionManagement来包含以下的函数
根据任务分析的步骤有:
1. 读取数据:
def read_teams_from_file(self, file_name):
with open(file_name, 'r') as f:
next(f) # 跳过标题行
for line in f:
parts = line.split('#')
team_id = parts[0].strip() # 去除队伍编号前后的空格
if team_id.isdigit(): # 检查队伍编号是否只包含数字字符
self.add_team(team_id, parts[1].strip(), parts[2].strip(), parts[3].strip(), parts[4].strip(),
parts[5].strip())
else:
print(f"Invalid team ID: {team_id}")
2.数据管理:
在这里我们需要有增加,修改和删除不同的功能。
1):增加
def add_team(self, id, name, school, competition_type, participants, mentor):
if id not in self.teams:
self.teams[int(id)] = Team(id, name, school, competition_type, participants, mentor)
# 创建一个 Team 对象并将其添加到 self.teams 字典中
self._bst.insert(int(id)) # 将队伍编号添加到二叉排序树中
print(f"Team {id} added.")
else:
print(f"Team {id} already exists.")
if competition_type not in self._competitions:
self._competitions[competition_type] = deque()
self._competitions[competition_type].append(self.teams[int(id)])
2):删除
def delete_team(self):
team_id_to_delete = input("请输入要删除的参赛队编号:")
self._bst.delete(team_id_to_delete)
del self.teams[int(team_id_to_delete)]
3):修改(修改即删除原本的再增加一个,即在1和2基础上完成)
def modify_team(self):
team_id_to_modify = input("请输入要修改的参赛队编号:")
name = input("请输入新的参赛作品名称:")
school = input("请输入新的参赛学校:")
competition_type = input("请输入新的赛事类别:")
participants = input("请输入新的参赛者:")
mentor = input("请输入新的指导老师:")
self._bst.modify(team_id_to_modify, name, school, competition_type, participants, mentor)
del self.teams[int(team_id_to_modify)]
# self.teams[team_id_to_modify] = Team(team_id_to_modify, name, school, competition_type, participants, mentor)
self.add_team(team_id_to_modify, name, school, competition_type, participants, mentor)
3.赛事类别:
这个被包含在了在1)读取数据时.
4.界面(包含后面代码的一些界面,在这里一起给出):
competition_management = CompetitionManagement()
competition_management.read_teams_from_file("D:\\QQ download\\team.txt")
print("赛事管理系统:")
print("0.查看参赛队伍")
print("1.增加参赛队伍")
print("2.删除参赛队伍")
print("3.修改参赛队伍")
print("4.查找参赛队伍")
print("5.按参赛学校查询参赛队伍")
print("6.决赛叫号系统")
print("7.校园导游系统")
print("#键取消")
flag = input("请输入你要使用的系统标号:")
while (flag != '#'):
if flag == '0':
competition_management.display_teams()
elif flag == '1':
id = input("请输入要增加的参赛队编号(编号请输入数字):")
name = input("请输入新的参赛作品名称:")
school = input("请输入新的参赛学校:")
competition_type = input("请输入新的赛事类别:")
participants = input("请输入新的参赛者:")
mentor = input("请输入新的指导老师:")
competition_management.add_team(id, name, school, competition_type, participants, mentor)
elif flag == '2':
competition_management.delete_team()
elif flag == '3':
competition_management.modify_team()
elif flag == '4':
competition_management.read_inf_from_tree()
elif flag == '5':
competition_management.display_teams_by_school()
elif flag == '6':
competition_management.simulate_competitions()
elif flag == '7':
campus = Campus()
print("下面是可查询的信息地点:")
print("行政楼|海韵湖|图书馆|东食堂|东操场|南门|文体中心|西操场|经世楼|文理大楼|西食堂|西宿舍区")
campus.set_location()
campus.printInfo()
campus.print_shortest_distance()
print("\n赛事管理系统:")
print("0.查看参赛队伍")
print("1.增加参赛队伍")
print("2.删除参赛队伍")
print("3.修改参赛队伍")
print("4.查找参赛队伍")
print("5.按参赛学校查询参赛队伍")
print("6.决赛叫号系统")
print("7.校园导游系统")
print("#键退出")
flag = input("请输入你要使用的系统标号:")
print("感谢使用赛事管理系统")
实现基于二叉排序树的查找。根据提示输入参赛队编号,若查找成功,输出该赛事类别对应的基本信息(参赛作品名称、参赛学校、赛事类别、参赛者和指导老师信息),同时,输出查找成功时的平均查找长度ASL;否则,输出“查找失败!”。请输出ASL的计算表达式和结果值。
问题的步骤可以被分解为以下几个部分:
创建二叉排序树:根据读入的数据,创建一个二叉排序树,使每个节点都包含一个参赛队的信息,节点的键是参赛队编号。
进行查找:当接收到一个参赛队编号输入时,从二叉排序树中查找这个编号。这可以通过比较输入的编号和树的节点来实现。如果输入的编号小于某个节点,那么就在这个节点的左子树中继续查找;如果输入的编号大于某个节点,那么就在这个节点的右子树中继续查找;如果输入的编号等于某个节点,那么查找成功。
处理查找结果:如果查找成功,输出这个节点包含的参赛队的信息,并计算和输出平均查找长度(Average Search Length,ASL);如果查找失败,输出“查找失败!”。平均查找长度(ASL)是一个度量查找效率的指标,它代表了平均查找一个节点需要比较的次数。对于二叉排序树来说,ASL可以通过下面的公式计算:
ASL = (总的查找路径长度 / 节点数量)
对于二叉树首先先定义结点,创建二叉排序树:
class Node:
def __init__(self, team_id):
self.team_id = team_id
self.left = None
self.right = None
插入结点:
def insert(self, team_id):
if not self.root: # 树为空,即根节点为 None,则将新节点作为根节点插入
self.root = Node(team_id)
else: # 树不为空,则调用 _insert 方法来递归地在合适的位置插入新节点
self._insert(self.root, team_id)
def _insert(self, node, team_id):
if int(team_id) < int(node.team_id):
if node.left is None:
node.left = Node(team_id)
else:
self._insert(node.left, team_id)
elif int(team_id) > int(node.team_id):
if node.right is None:
node.right = Node(team_id)
else:
self._insert(node.right, team_id)
删除节点:
def delete(self, team_id):
self.root = self._delete_node(self.root, team_id)
def _delete_node(self, node, team_id):
if node is None:
return None
if int(team_id) < node.team_id:
node.left = self._delete_node(node.left, team_id)
elif int(team_id) > node.team_id:
node.right = self._delete_node(node.right, team_id)
else:
if node.left is None:
return node.right
elif node.right is None:
return node.left
else:
min_node = self._find_min_node(node.right)
node.team_id = min_node.team_id
node.right = self._delete_node(node.right, min_node.team_id)
return node
def _find_min_node(self, node):
current = node
while current.left is not None:
current = current.left
return current
修改结点:
def modify(self, team_id, name=None, school=None, competition_type=None, participants=None, mentor=None):
self.root = self._modify_node(self.root, team_id, name, school, competition_type, participants, mentor)
def _modify_node(self, node, team_id, name, school, competition_type, participants, mentor):
if node is None:
return None
if int(team_id) < int(node.team_id):
node.left = self._modify_node(node.left, team_id, name, school, competition_type, participants, mentor)
elif int(team_id) > int(node.team_id):
node.right = self._modify_node(node.right, team_id, name, school, competition_type, participants, mentor)
else:
if name is not None:
node.name = name
if school is not None:
node.school = school
if competition_type is not None:
node.competition_type = competition_type
if participants is not None:
node.participants = participants
if mentor is not None:
node.mentor = mentor
return node
然后可以进行第二步:
进行查找;查找时从二叉排序树中查找这个编号。这可以通过比较输入的编号和树的节点来实现。如果输入的编号小于某个节点,那么就在这个节点的左子树中继续查找;如果输入的编号大于某个节点,那么就在这个节点的右子树中继续查找;如果输入的编号等于某个节点,那么查找成功。
函数如下:
def find_info(self, team_id):
node = self._find_node(self.root, team_id)
if node is not None:
team = competition_management.teams[node.team_id]
return team.name, team.school, team.competition_type, team.participants, team.mentor
else:
return None
def _find_node(self, node, team_id):
if node is None or team_id == node.team_id:
return node
if int(team_id) < int(node.team_id):
return self._find_node(node.left, team_id)
else:
return self._find_node(node.right, team_id)
接下来进行第三步,也就是计算ASL:平均查找长度(ASL)是一个度量查找效率的指标,它代表了平均查找一个节点需要比较的次数。对于二叉排序树来说,ASL可以通过下面的公式计算:ASL = (总的查找路径长度 / 节点数量)
函数如下:
def calculate_asl(self):
count = [0, 0] # count[0] 存储累加的深度,count[1] 存储节点总数
self._calculate_asl(self.root, 1, count)
return count[0] / count[1] if count[1] > 0 else 0
def _calculate_asl(self, node, depth, count):
if node is not None:
count[0] += depth # 累加路径长度
count[1] += 1 # 增加节点计数
self._calculate_asl(node.left, depth + 1, count)
self._calculate_asl(node.right, depth + 1, count)
能够提供按参赛学校查询参赛团队,根据提示输入参赛学校名称,若查找成功,输出该学校参赛的所有团队的基本信息,输出的参赛团队需有序输出(按参赛队编号)。(排序算法可从选择排序、插入排序、希尔排序、归并排序、堆排序中任意选择,并为选择算法的原因做出说明。)
此任务的步骤可以分解如下:
查找:在收到输入的参赛学校名称后,你需要查找所有来自该学校的参赛团队。这可以通过遍历存储团队信息的数据结构(比如说一个列表或一个二叉搜索树)并检查每个团队的学校名称是否与输入的名称匹配来完成。
排序:在你找到所有符合条件的团队后,你需要对它们进行排序,排序依据是参赛队编号。你可以选择各种排序算法(如选择排序、插入排序、希尔排序、归并排序、堆排序)来完成这个任务。选择哪种排序算法可能取决于团队数量和编号的分布。
输出:一旦团队被排序,你可以按顺序输出每个团队的基本信息。
对于第一步查找:
有如下函数,根据学校来查找其他相关信息
def display_teams_by_school(self):
school = input("请输入你要查找的学校:")
teams = self.find_teams_by_school(school)
if len(teams) > 0:
print(f"查询的参赛学校:{school}")
print("-------------------------------------------------------")
for team in teams:
print(f"参赛队编号:{team.id}")
print(f"参赛作品名称:{team.name}")
print(f"参赛学校:{team.school}")
print(f"赛事类别:{team.competition_type}")
print(f"参赛者:{team.participants}")
print(f"指导老师:{team.mentor}")
print()
else:
print(f"未找到参赛学校:{school}")
排序:
Python有自带函数sort,可以自动将ID从小到大排序。在这里十分方便:
teams.sort(key=lambda team: team.id) # 按参赛队编号排序
输出:
if len(teams) > 0:
print(f"查询的参赛学校:{school}")
print("-------------------------------------------------------")
for team in teams:
print(f"参赛队编号:{team.id}")
print(f"参赛作品名称:{team.name}")
print(f"参赛学校:{team.school}")
print(f"赛事类别:{team.competition_type}")
print(f"参赛者:{team.participants}")
print(f"指导老师:{team.mentor}")
print()
为省赛现场设计一个决赛叫号系统。所有参赛队按赛事组织文件中的赛事类别分到9个决赛室,决赛室按顺序叫号,被叫号参赛队进场,比赛结束后,下一参赛队才能进赛场。请模拟决赛叫号系统,演示省赛现场各决赛室的参赛队进场情况。(模拟时,各参赛队进场比赛时间可设为0.5秒)
这个任务需要你设计一个叫号系统来管理决赛队伍的进场顺序。在这个系统中,参赛队伍被分到9个不同的决赛室,每个决赛室依次叫号,只有在前一个队伍完成比赛后,下一个队伍才能进场。我们可以通过以下步骤来分析这个任务:
数据管理:首先,你需要一种方式来管理参赛队伍的数据。每个队伍都应该有一个赛事类别,这将决定它们去决赛室的顺序。这可能需要一个类或结构来存储队伍的信息,以及一个列表或数组来存储这些队伍。
创建决赛室:你需要创建9个决赛室,每个决赛室都有一个队列来管理它的队伍。当一个队伍的比赛结束时,它应该从队列中移除,下一个队伍则进入比赛。
模拟叫号和比赛:你需要一种方式来模拟叫号和比赛的过程。这可能意味着你需要一个循环来遍历每个决赛室,以及一个延时函数来模拟队伍比赛的时间(在这个任务中,每个队伍的比赛时间被设定为0.5秒)。
输出:你可能需要输出每个决赛室的状态,包括哪个队伍正在比赛,哪些队伍在等待,以及比赛的进程等。
数据管理:
import time
from collections import deque
def __init__(self):
self.teams = {}
self._bst = BinarySearchTree()
self._competitions = {}
self._final_rooms = {} # 决赛室字典
创建决赛室和模拟叫号和比赛:
def simulate_competitions(self):
for competition_type, team_queue in self._competitions.items():
room_number = self._final_rooms.get(competition_type, 0) # 获取决赛室编号,如果没有指定,默认为0
while team_queue:
team = team_queue.popleft()
room_number += 1;
room_number = room_number % 9
if room_number == 0:
room_number = 9
print(
f"Team {team.id} from {team.school} is now competing in Room {room_number} for {competition_type}.")
time.sleep(0.5)
print(f"All teams for competition {competition_type} have competed.")
输出状态:
while team_queue:
team = team_queue.popleft()
room_number += 1;
room_number = room_number % 9
if room_number == 0:
room_number = 9
print(
f"Team {team.id} from {team.school} is now competing in Room {room_number} for {competition_type}.")
time.sleep(0.5)
print(f"All teams for competition {competition_type} have competed.")
结束状态:当所有队尾完成后自动结束.
这个任务需要你开发一个校园导游系统,为参赛者提供校园地图中的目标地点(例如建筑物)信息查询和路径导航服务。任务可以按照以下步骤分解:
建立地点信息数据库:你需要收集并储存至少10个目标地点的相关信息。这些信息可能包括名称、位置、功能、历史等。
路径查询:你需要为用户提供从一个地点到另一个地点的最短路径查询服务。这可能需要用到图算法,如Dijkstra算法或A*算法,来找到两点间的最短路径。你需要建立一个代表校园地图的图结构,其中每个节点代表一个地点,每个边代表两个地点之间的路径,边的权值可以代表路径的长度。在这里我用到了Dijkstra算法:
以下是Dijkstra算法的基本步骤:
1.创建一个空的已访问节点集合和一个初始节点集合。初始节点的距离设为0,其余节点的距离设为无穷大。
2.从未访问节点集合中选择一个距离最小的节点,将其加入到已访问节点集合中,并从未访问节点集合中移除。
3.更新所有从新加入的节点可以直接到达的未访问节点的距离。如果从新加入的节点到某个未访问节点的距离小于该未访问节点当前的距离,那么就更新该未访问节点的距离。
4.重复第2步和第3步,直到已访问节点集合包含所有节点,或者已经找到目标节点。
在这个过程中,每个节点都有一个距离值,表示从起始节点到这个节点的最短路径长度。同时,每个节点还可以记录一个前驱节点,表示这个最短路径是从哪个节点来的。这样,当算法完成时,我们就可以通过回溯前驱节点,得到从起始节点到任何节点的最短路径。
信息查询接口:你需要为用户提供一个接口,他们可以通过这个接口查询任意目标地点的相关信息,也可以查询任意两个地点之间的最短路径。
信息输出:你需要设计一种方法来展示查询结果。对于地点信息查询,你可以直接展示地点的相关信息;对于路径查询,你可能需要以某种形式(如列表或地图)展示出路径。
建立地点信息数据库:
下面给出校园景点的无向带权图:
图1 校园景点的无向带权图
def set_location(self):
campus.add_location("文理大楼", {"行政楼": 700, "经世楼": 100,"图书馆":400},
"这是文理大楼,是老师们的办公室,同时也是自习室和教室")
campus.add_location("经世楼", {"文理大楼": 100, "西操场": 100},
"这是经世楼,是学生的教室,你也可以把这里当作自习室")
campus.add_location("西宿舍区", {"西食堂": 200, "南门": 700},
"这里是西宿舍区,是学生睡觉的地方")
campus.add_location("行政楼", {"文理大楼": 700, "海韵湖": 300},
"这里是行政楼,是行政老师办公的地方")
campus.add_location("海韵湖", {"行政楼": 300, "图书馆": 600},
"这里是海韵湖,这里有很好的风光可以欣赏")
campus.add_location("西操场", {"经世楼": 100, "图书馆": 550,"西食堂":250,"文体中心":100},
"这里是西操场,在这里你可以跑步,散步来进行锻炼")
campus.add_location("图书馆", {"海韵湖": 600, "文理大楼": 400, "西操场": 550,"东食堂":100},
"这是图书馆,你可以在这里面安静的上自习")
campus.add_location("文体中心", {"西食堂": 300, "西操场": 100, "南门": 250,"东食堂":550},
"这是文体中心,里面有乒乓球馆,健身馆,旁边还有游泳馆,你可以来这里挥洒汗水")
campus.add_location("东食堂", {"图书馆": 100, "文体中心": 550, "东操场": 100},
"这里是东食堂,里面有可口的饭菜,东区的学生一般喜欢在这里吃")
campus.add_location("东操场", {"东食堂": 100, "南门": 250},
"这里是东操场,在这里你可以跑步,散步来进行锻炼")
campus.add_location("南门", {"东操场": 250, "文体中心": 250, "西宿舍区": 700},
"这里是南门,你可以从这里出校门")
campus.add_location("西食堂", {"西操场": 250, "西宿舍区": 200, "文体中心": 300},
"这里是西食堂,里面有可口的饭菜,这里三楼是民族食堂")
路径查询:为用户提供从一个地点到另一个地点的最短路径查询服务,在这里我用到了Dijkstra算法,代码实现如下:
def find_shortest_path(self, start, end):
distances = {location: float('infinity') for location in self.map}
distances[start] = 0
previous_locations = {location: None for location in self.map}
paths = {location: [] for location in self.map}
locations = list(self.map.keys())
while locations:
current_location = min(locations, key=lambda location: distances[location])
if distances[current_location] == float('infinity'):
break
for neighbor, distance in self.map[current_location].items():
new_distance = distances[current_location] + distance
if new_distance < distances[neighbor]:
distances[neighbor] = new_distance
previous_locations[neighbor] = current_location
paths[neighbor] = paths[current_location] + [current_location]
locations.remove(current_location)
shortest_distance = distances[end]
shortest_path = paths[end] + [end]
return shortest_distance, shortest_path
信息查询接口和信息输出:
1.增加location:
def add_location(self, location, connections, info=None):
self.map[location] = connections
if info is not None:
self.locations_info[location] = info
2.根据location输出其对应的信息:
def find_location_info(self, location):
if location in self.locations_info:
return self.locations_info[location]
else:
return "Location information not available."
3.输出相关信息:
def printInfo(self):
location = input("请输入要查询信息的地点:")
location_info = campus.find_location_info(location)
print(f"{location} 详细信息:")
print(location_info)
def print_shortest_distance(self):
# 查询两地之间的最短路径
location1 = input("查询的目的地起点:")
location2 = input("查询的目的地终点:")
shortest_path = campus.find_shortest_path(location1, location2)
print(f"Shortest Path from {location1} to {location2}:")
print(shortest_path)