目录
1.任务定义
1.1问题描述和要求
【问题描述】 设计一个简单的赛事管理系统。
【基本要求】
(1)从team.txt中读取参赛队伍的基本信息,能够管理各参赛队的基本信息(包含参赛队编号,参赛作品名称,参赛学校,赛事类别,参赛者,指导老师),赛事类别共11项(参见大赛官网jsjds.blcu.edu.cn);包括增加、删除、修改参赛队伍的信息。
(2)实现基于二叉排序树的查找。根据提示输入参赛队编号,若查找成功,输出该赛事类别对应的基本信息(参赛作品名称、参赛学校、赛事类别、参赛者和指导老师信息),同时,输出查找成功时的平均查找长度ASL;否则,输出“查找失败!”。请输出ASL的计算表达式和结果值。
(3)能够提供按参赛学校查询参赛团队,根据提示输入参赛学校名称,若查找成功,输出该学校参赛的所有团队的基本信息,输出的参赛团队需有序输出(按参赛队编号)。(排序算法可从选择排序、插入排序、希尔排序、归并排序、堆排序中任意选择,并为选择算法的原因做出说明。)
(4)为省赛现场设计一个决赛叫号系统。所有参赛队按赛事组织文件中的赛事类别分到9个决赛室,决赛室按顺序叫号,被叫号参赛队进场,比赛结束后,下一参赛队才能进赛场。请模拟决赛叫号系统,演示省赛现场各决赛室的参赛队进场情况。(模拟时,各参赛队进场比赛时间可设为0.5秒)
(5)赛事系统为参赛者提供赛地的校园导游程序。为参赛者提供各种路径导航的查询服务。以我校长山校区提供比赛场地为例,(请为参赛者提供不少于10个目标地的导航。可为参赛者提供校园地图中任意目标地(建筑物)相关信息的查询;提供图中任意目标地(建筑物)的问路查询,即查询任意两个目的地(建筑物)之间的一条最短的简单路径。
2.问题分析
参赛队伍管理:可以使用数据结构如数组、链表或哈希表来存储参赛队伍的基本信息。每个参赛队伍可以用一个结构体或类来表示,包含参赛队编号、参赛作品名称、参赛学校、赛事类别、参赛者和指导老师等属性。通过数组或链表来管理参赛队伍,实现增加、删除和修改参赛队伍的操作。
参赛队伍信息查询:可以使用二叉排序树(BST)来实现参赛队伍信息的查找。每个节点存储一个参赛队伍的信息,按照参赛队编号进行排序。通过比较输入的参赛队编号与BST中节点的值来进行查找操作。记录查找过程中的比较次数,用于计算平均查找长度ASL。
参赛团队查询:对于按参赛学校查询参赛团队或按赛事类别查询参赛团队,可以使用适当的排序算法如选择排序、插入排序、希尔排序、归并排序或堆排序对参赛队伍进行排序。根据输入的参赛学校名称或赛事类别,在排序后的参赛队伍中进行顺序查找,并按照赛事类别有序输出参赛团队的基本信息。
决赛叫号系统:可以使用队列来实现决赛叫号系统。将参赛队伍按照赛事类别分配到不同的队列中,每个队列表示一个决赛室。按照指定的顺序依次叫号,每次从相应队列中出队一个参赛队伍,表示该队伍进场。确保叫号顺序与参赛队伍的进场秩序一致。
校园导游程序:针对校园导游功能,可以使用图的数据结构和相应的路径搜索算法。校园地图可以表示为一个带权有向图,建筑物可以表示为图的节点,路径长度可以表示为图的边的权重。通过图的遍历算法如深度优先搜索(DFS)或广度优先搜索(BFS)来提供建筑物相关信息的查询。对于查询任意两个目标地之间的一条最短路径,可以使用最短路径算法如Dijkstra算法或A*算法。
3.概要设计
3.1数据结构设计和功能设计:
3.1.1参赛队伍信息,校园导航、二叉树的数据结构:结构体——Team 用于存储参赛队伍的详细信息,包括队伍编号、作品名称、参赛学校等等。在利用前面已经说明的参赛队伍信息的数据结构的前提下,添加二叉树的数据结构,结构体——TreeNode 用于存储二叉树的左子树指针、右子树指针、构造函数等等。建筑信息的数据结构:结构体——CampusLcation 用于存储各个建筑物的详细信息,如建筑物的名称、代号、介绍和建筑物之间的距离。图的数据结构:vector<CampusLocation> locations用于存储校园建筑信息的动态数组。提供了校园导航中的十个建筑物的位置信息和两地之间的距离长度
struct Team {
int id; //队伍编号
string name; //作品名称
string school; //队伍学校
string category; //赛事类别
string members; //参赛者
string teacher; //指导老师
};
struct TreeNode {
Team team;
TreeNode* left; //左子树指针
TreeNode* right; //右子树指针
TreeNode(const Team& t) : team(t), left(nullptr), right(nullptr) {} //构造函数
};
struct CampusLocation {
string name; //名称
string code; //代号
string description; //介绍
map<string, int> distances; //相隔距离
};
3.1.2选择参赛队伍管理操作,根据main函数中的switch语句调用函数,其中输入1、2、3则是进行赛事队伍信息的增加、删除、修改。
void add_team(vector<Team>& teams) {
Team newTeam;
// 获取新队伍的信息
cout << "请输入参赛队编号:";
cin >> newTeam.id;
// 检查队伍编号是否已经存在
for (const Team& team : teams) {
if (team.id == newTeam.id) {
cout << "队伍编号已经存在,无法添加!" << endl;
return;
}
}
cout << "请输入参赛作品名称:";
cin.ignore(); // 忽略之前的换行符
getline(cin, newTeam.name);
cout << "请输入参赛学校:";
getline(cin, newTeam.school);
cout << "请输入赛事类别:";
getline(cin, newTeam.category);
cout << "请输入参赛者:";
getline(cin, newTeam.members);
cout << "请输入指导教师:";
getline(cin, newTeam.teacher);
// 将新队伍添加到向量中
teams.push_back(newTeam);
// 将新队伍写入文件
ofstream output("D:/qq/651847595/FileRecv/team.txt", ios::app);
if (output.is_open()) {
output << newTeam.id << " # " << newTeam.name << " # " << newTeam.school << " # " << newTeam.category << " # " << newTeam.members << " # " << newTeam.teacher << endl;
output.close();
cout << "添加成功!" << endl;
}
else {
cout << "无法打开文件进行写入!" << endl;
}
}
void delete_team(vector<Team>& teams) {
int id;
cout << "请输入要删除的队伍编号:";
cin >> id;
// 在向量中查找要删除的队伍
auto it = find_if(teams.begin(), teams.end(), [id](const Team& team) {
return team.id == id;
});
if (it != teams.end()) {
// 在文件中删除队伍信息
ofstream output("D:/qq/651847595/FileRecv/team.txt");
if (output.is_open()) {
for (const auto& team : teams) {
if (team.id != id) {
output << team.id << " # " << team.name << " # " << team.school << " # " << team.category << " # " << team.members << " # " << team.teacher << endl;
}
}
output.close();
cout << "删除成功!" << endl;
}
else {
cout << "无法打开文件进行写入!" << endl;
}
// 在向量中删除队伍
teams.erase(it);
}
else {
cout << "未找到对应的队伍编号,删除失败!" << endl;
}
}
void modify_team(vector<Team>& teams) {
int id;
cout << "请输入要修改的队伍编号:";
cin >> id;
// 在向量中查找要修改的队伍
auto it = find_if(teams.begin(), teams.end(), [id](const Team& team) {
return team.id == id;
});
if (it != teams.end()) {
Team& team = *it;
// 输出当前队伍信息
cout << "当前队伍信息:" << endl;
cout << "参赛队编号:" << team.id << endl;
cout << "参赛作品名称:" << team.name << endl;
cout << "参赛学校:" << team.school << endl;
cout << "赛事类别:" << team.category << endl;
cout << "参赛者:" << team.members << endl;
cout << "指导教师:" << team.teacher << endl;
// 获取用户输入的修改信息
cout << "请输入新的参赛作品名称(按回车键保持不变):";
cin.ignore(); // 忽略之前的换行符
string newName;
getline(cin, newName);
cout << "请输入新的参赛学校(按回车键保持不变):";
string newSchool;
getline(cin, newSchool);
cout << "请输入新的赛事类别(按回车键保持不变):";
string newCategory;
getline(cin, newCategory);
cout << "请输入新的参赛者(按回车键保持不变):";
string newMembers;
getline(cin, newMembers);
cout << "请输入新的指导教师(按回车键保持不变):";
string newTeacher;
getline(cin, newTeacher);
// 更新队伍信息
if (!newName.empty()) {
team.name = newName;
}
if (!newSchool.empty()) {
team.school = newSchool;
}
if (!newCategory.empty()) {
team.category = newCategory;
}
if (!newMembers.empty()) {
team.members = newMembers;
}
if (!newTeacher.empty()) {
team.teacher = newTeacher;
}
// 将修改后的队伍写入文件
ofstream output("D:/qq/651847595/FileRecv/team.txt");
if (output.is_open()) {
for (const auto& t : teams) {
output << t.id << " # " << t.name << " # " << t.school << " # " << t.category << " # " << t.members << " # " << t.teacher << endl;
}
output.close();
cout << "修改成功!" << endl;
}
else {
cout << "无法打开文件进行写入!" << endl;
}
}
else {
cout << "未找到对应的队伍编号,修改失败!" << endl;
}
}
3.1.3选择基于二叉树查找队伍信息操作,根据main函数中的switch语句调用函数,在命令行中输入4调用函数find_team_by_id。
void insertTeam(TreeNode*& root, const Team& team) {
if (root == nullptr) {
root = new TreeNode(team);
return;
}
if (team.id < root->team.id) {
insertTeam(root->left, team);
}
else {
insertTeam(root->right, team);
}
}
void searchTeam(TreeNode* root, int id, int level, int depth, int& count, int& nodeCount, int& sumDepths) {
if (root == nullptr) {
return;
}
count++;
if (root->team.id == id) {
nodeCount++;
sumDepths += depth;
return;
}
else if (id < root->team.id) {
searchTeam(root->left, id, level + 1, depth + 1, count, nodeCount, sumDepths);
}
else {
searchTeam(root->right, id, level + 1, depth + 1, count, nodeCount, sumDepths);
}
}
void calculateASL(TreeNode* node, int depth, int& nodeCount, int& sumDepths) {
if (node == nullptr) {
return;
}
nodeCount++;
sumDepths += depth;
calculateASL(node->left, depth + 1, nodeCount, sumDepths);
calculateASL(node->right, depth + 1, nodeCount, sumDepths);
}
void find_team_by_id(const vector<Team>& teams) {
int id;
cout << "请输入要查找的队伍编号:";
cin >> id;
TreeNode* root = nullptr;
// 构建二叉排序树
for (const Team& team : teams) {
insertTeam(root, team);
}
int count = 0; // 查找次数
int nodeCount = 0; // 节点总数
int sumDepths = 0; // 节点深度总和
int totalNodeCount = 0; // 总节点数
int totalSumDepths = 0; // 总节点深度之和
searchTeam(root, id, 1, 1, count, nodeCount, sumDepths);
if (nodeCount > 0) {
const Team& team = root->team;
cout << "队伍信息:" << endl;
cout << "参赛队编号:" << team.id << endl;
cout << "参赛作品名称:" << team.name << endl;
cout << "参赛学校:" << team.school << endl;
cout << "赛事类别:" << team.category << endl;
cout << "参赛者:" << team.members << endl;
cout << "指导教师:" << team.teacher << endl;
// 计算平均查找长度ASL
float asl = static_cast<float>(sumDepths) / nodeCount;
cout << "平均查找长度ASL:" << asl << endl;
// 计算整个二叉树中的ASL
calculateASL(root, 1, totalNodeCount, totalSumDepths);
// 计算二叉树中所有节点的深度之和除以节点总数(ASL)
float totalASL = static_cast<float>(totalSumDepths) / totalNodeCount;
cout << "整个二叉树中的ASL:" << totalASL << endl;
}
else {
cout << "查找失败!" << endl;
// 计算二叉树中所有节点的深度之和除以节点总数(ASL)
calculateASL(root, 1, totalNodeCount, totalSumDepths);
// 计算二叉树中所有节点的深度之和除以节点总数(ASL)
float totalASL = static_cast<float>(totalSumDepths) / totalNodeCount;
cout << "二叉树中所有节点的深度之和除以节点总数(ASL):" << totalSumDepths << " / " << totalNodeCount << " = " << totalASL << endl;
}
}
3.1.4参赛队伍查询:选择参赛队伍查询操作,根据main函数中的switch语句调用函数,其中输入5、6则是根据参赛学校和赛事类别查询队伍信息。
void find_teams_by_school(const vector<Team>& teams) {
string school;
cout << "请输入要查找的学校名称:";
cin.ignore(); // 忽略之前的换行符
getline(cin, school);
// 在向量中查找队伍
vector<const Team*> foundTeams;
for (const Team& team : teams) {
if (team.school == school) {
foundTeams.push_back(&team);
}
}
if (!foundTeams.empty()) {
cout << "找到的队伍:" << endl;
for (const Team* team : foundTeams) {
cout << "参赛队编号:" << team->id << endl;
cout << "参赛作品名称:" << team->name << endl;
cout << "参赛学校:" << team->school << endl;
cout << "赛事类别:" << team->category << endl;
cout << "参赛者:" << team->members << endl;
cout << "指导教师:" << team->teacher << endl;
cout << endl;
}
}
else {
cout << "未找到对应学校的队伍!" << endl;
}
}
void find_teams_by_category(const vector<Team>& teams) {
string category;
cout << "请输入要查找的赛事类别:";
cin.ignore(); // 忽略之前的换行符
getline(cin, category);
// 在向量中查找队伍
vector<const Team*> foundTeams;
for (const Team& team : teams) {
if (team.category == category) {
foundTeams.push_back(&team);
}
}
if (!foundTeams.empty()) {
cout << "找到的队伍:" << endl;
for (const Team* team : foundTeams) {
cout << "参赛队编号:" << team->id << endl;
cout << "参赛作品名称:" << team->name << endl;
cout << "参赛学校:" << team->school << endl;
cout << "赛事类别:" << team->category << endl;
cout << "参赛者:" << team->members << endl;
cout << "指导教师:" << team->teacher << endl;
cout << endl;
}
}
else {
cout << "未找到对应赛事类别的队伍!" << endl;
}
}
3.1.5决赛叫号模拟:九个决赛室的队伍依次从1号决赛室到9号决赛室的顺序进行叫号,并且用sleep_for函数停顿3秒模拟每次叫号的进场和比赛时间。
void simulate_final_calling(const vector<Team>& teams) {
map<string, vector<Team>> groupedTeams;
// 将队伍按赛事类别分组
for (const Team& team : teams) {
groupedTeams[team.category].push_back(team);
}
// 按赛事类别分组的队伍排序
for (auto& entry : groupedTeams) {
sort(entry.second.begin(), entry.second.end(), [](const Team& a, const Team& b) {
return a.id < b.id;
});
}
// 模拟决赛叫号系统
int roomNumber = 1;
for (const auto& entry : groupedTeams) {
const string& category = entry.first;
const vector<Team>& teamsInCategory = entry.second;
cout << "赛事类别:" << category << endl;
cout << "决赛室叫号顺序:" << endl;
for (const Team& team : teamsInCategory) {
cout << "队伍编号:" << team.id << endl;
cout << "参赛作品名称:" << team.name << endl;
cout << "参赛学校:" << team.school << endl;
cout << "参赛者:" << team.members << endl;
cout << "指导老师:" << team.teacher << endl;
// 叫号
cout << "决赛室 " << roomNumber << " 号,请队伍编号 " << team.id << " 进场" << endl;
this_thread::sleep_for(chrono::seconds(1));
// 进场
cout << "队伍编号 " << team.id << " 进场" << endl;
this_thread::sleep_for(chrono::seconds(3));
// 比赛结束
cout << "队伍编号 " << team.id << " 比赛结束" << endl;
this_thread::sleep_for(chrono::seconds(1));
cout << endl; // 添加空行
}
// 切换到下一个决赛室
roomNumber++;
cout << endl;
}
}
3.1.6校园导航:要进行校园导航时,在操作面板中选择8,调用campus_guide函数,开始进行校园导航,再通过选择0、1、2,选择返回上级菜单、查询建筑物信息、导航查询。首先在选择校园导航时,会输出所有建筑物的名称和代号以便后面进行导航的方便。
void display_location_info(const CampusLocation& location) {
cout << "建筑物名称:" << location.name << endl;
cout << "建筑物编号:" << location.code << endl;
cout << "建筑物描述:" << location.description << endl;
cout << endl;
}
void campus_guide(const vector<CampusLocation>& locations) {
cout << "校园建筑物信息:" << endl;
for (const auto& location : locations) {
cout << "建筑物名称:" << location.name << endl;
cout << "建筑物编号:" << location.code << endl;
cout << endl;
}
int choice = 0;
do {
cout << "请选择操作:" << endl;
cout << "1. 查询建筑物信息" << endl;
cout << "2. 导航查询" << endl;
cout << "0. 返回上级菜单" << endl;
cin >> choice;
switch (choice) {
case 1: {
string targetCode;
cout << "请输入要查询的建筑物编号:";
cin >> targetCode;
bool found = false;
for (const auto& location : locations) {
if (location.code == targetCode) {
display_location_info(location);
found = true;
break;
}
}
if (!found) {
cout << "未找到对应的建筑物信息!" << endl;
}
break;
}
case 2: {
string startCode, endCode;
cout << "请输入起始建筑物编号:";
cin >> startCode;
cout << "请输入目标建筑物编号:";
cin >> endCode;
// 使用 Dijkstra 算法求解最短路径
map<string, int> distance;
map<string, string> previous;
priority_queue<pair<int, string>, vector<pair<int, string>>, greater<pair<int, string>>> pq;
// 初始化距离和前驱节点
for (const auto& location : locations) {
if (location.code == startCode) {
distance[location.code] = 0;
}
else {
distance[location.code] = numeric_limits<int>::max();
}
previous[location.code] = "";
}
pq.push(make_pair(0, startCode));
while (!pq.empty()) {
string currentCode = pq.top().second;
pq.pop();
for (const auto& neighbor : locations) {
if (neighbor.code == currentCode) {
for (const auto& edge : neighbor.distances) {
string neighborCode = edge.first;
int weight = edge.second;
int newDistance = distance[currentCode] + weight;
if (newDistance < distance[neighborCode]) {
distance[neighborCode] = newDistance;
previous[neighborCode] = currentCode;
pq.push(make_pair(newDistance, neighborCode));
}
}
break;
}
}
}
// 构建最短路径
vector<string> path;
string currentCode = endCode;
while (currentCode != startCode) {
path.push_back(currentCode);
currentCode = previous[currentCode];
}
path.push_back(startCode);
reverse(path.begin(), path.end());
// 输出最短路径和距离长度
cout << "最短路径:" << endl;
int totalDistance = 0;
for (size_t i = 0; i < path.size(); ++i) {
const string& code = path[i];
for (const auto& location : locations) {
if (location.code == code) {
cout << location.name;
if (i < path.size() - 1) {
cout << " -> ";
}
break;
}
}
if (i < path.size() - 1) {
totalDistance += distance[path[i]] - distance[path[i + 1]];
}
}
cout << "目标建筑物" << endl;
cout << "距离长度:" << abs(totalDistance) << endl;
break;
}
case 0:
cout << "返回上级菜单!" << endl;
break;
default:
cout << "无效输入,请重新选择!" << endl;
break;
}
} while (choice != 0);
}
下面给出校园景点的无向带权图:
main函数:
int main() {
vector<Team> teams;
ifstream input("D:/qq/651847595/FileRecv/team.txt", ios::binary);
if (input.is_open()) {
string line;
while (getline(input, line)) {
istringstream iss(line);
Team temp;
char discard; // 用于丢弃'#'分隔符
if (iss >> temp.id >> discard >> temp.name >> discard >> temp.school >> discard >> temp.category >> discard >> temp.members >> discard >> temp.teacher) {
teams.push_back(temp);
}
}
input.close();
}
vector<CampusLocation> locations;
//校园建筑信息
CampusLocation location1;
location1.name = "西宿舍区";
location1.code = "A";
location1.description = "西宿舍区是计算机学院的学生居住的地方";
locations.push_back(location1);
CampusLocation location2;
location2.name = "西苑食堂";
location2.code = "B";
location2.description = "西苑食堂是学生们吃饭的地方";
locations.push_back(location2);
CampusLocation location3;
location3.name = "明德园";
location3.code = "C";
location3.description = "明德园可以观看鲜花";
locations.push_back(location3);
CampusLocation location4;
location4.name = "文体中心";
location4.code = "D";
location4.description = "文体中心是学生门上体育课以及举办体育活动的地方";
locations.push_back(location4);
CampusLocation location5;
location5.name = "西操场";
location5.code = "E";
location5.description = "西操场是学生们运动的地方";
locations.push_back(location5);
CampusLocation location6;
location6.name = "文理大楼";
location6.code = "F";
location6.description = "纹理大楼是学生们上课和实验的地方";
locations.push_back(location6);
CampusLocation location7;
location7.name = "行政大楼";
location7.code = "G";
location7.description = "行政大楼是老师们开会和办公的地方";
locations.push_back(location7);
CampusLocation location8;
location8.name = "求索园";
location8.code = "H";
location8.description = "求索园是学生们休息娱乐的地方";
locations.push_back(location8);
CampusLocation location9;
location9.name = "东苑食堂";
location9.code = "I";
location9.description = "东苑食堂是东区的学生们吃饭的地方";
locations.push_back(location9);
CampusLocation location10;
location10.name = "图书馆";
location10.code = "J";
location10.description = "图书馆是学生们借阅书籍的地方";
locations.push_back(location10);
// 建筑物之间的距离信息
locations[0].distances["A"] = 0;
locations[0].distances["B"] = 200;
locations[0].distances["D"] = 700;
locations[1].distances["A"] = 200;
locations[1].distances["B"] = 0;
locations[1].distances["C"] = 200;
locations[1].distances["D"] = 300;
locations[1].distances["E"] = 200;
locations[2].distances["B"] = 200;
locations[2].distances["C"] = 0;
locations[2].distances["E"] = 100;
locations[2].distances["F"] = 150;
locations[3].distances["A"] = 700;
locations[3].distances["B"] = 300;
locations[3].distances["D"] = 0;
locations[3].distances["E"] = 100;
locations[4].distances["B"] = 200;
locations[4].distances["C"] = 100;
locations[4].distances["D"] = 100;
locations[4].distances["E"] = 0;
locations[4].distances["H"] = 100;
locations[4].distances["I"] = 600;
locations[5].distances["C"] = 150;
locations[5].distances["F"] = 0;
locations[5].distances["G"] = 700;
locations[5].distances["H"] = 150;
locations[5].distances["J"] = 400;
locations[6].distances["F"] = 700;
locations[6].distances["G"] = 0;
locations[6].distances["J"] = 900;
locations[7].distances["E"] = 100;
locations[7].distances["F"] = 150;
locations[7].distances["H"] = 0;
locations[7].distances["I"] = 150;
locations[7].distances["J"] = 100;
locations[8].distances["E"] = 600;
locations[8].distances["H"] = 150;
locations[8].distances["I"] = 0;
locations[8].distances["J"] = 100;
locations[9].distances["F"] = 400;
locations[9].distances["G"] = 900;
locations[9].distances["H"] = 100;
locations[9].distances["I"] = 100;
locations[9].distances["J"] = 0;
int choice = 0;
do {
cout << "请输入您要进行的操作:" << endl;
cout << "1. 添加队伍" << endl;
cout << "2. 删除队伍" << endl;
cout << "3. 修改队伍信息" << endl;
cout << "4. 根据队伍编号查找队伍" << endl;
cout << "5. 根据学校查找队伍" << endl;
cout << "6. 根据赛事类别查找队伍" << endl;
cout << "7. 模拟决赛叫号系统" << endl;
cout << "8. 校园导游" << endl;
cout << "0. 退出系统" << endl;
cin >> choice;
switch (choice) {
case 1:
add_team(teams);
break;
case 2:
delete_team(teams);
break;
case 3:
modify_team(teams);
break;
case 4:
find_team_by_id(teams);
break;
case 5:
find_teams_by_school(teams);
break;
case 6:
find_teams_by_category(teams);
break;
case 7:
simulate_final_calling(teams);
break;
case 8:
campus_guide(locations);
break;
case 0:
cout << "谢谢使用!" << endl;
break;
default:
cout << "无效输入,请重新输入!" << endl;
break;
}
} while (choice != 0);
return 0;
}