数据结构课程设计——计设参赛系统

目录

一、课程设计内容

二、问题分析

三、概要设计

         数据结构设计

函数方法与成员变量设计

系统页面设置

   四、总结

附录


一、课程设计内容

  本次课程设计要求协助中国大学生计算机设计大赛江苏省组委会,设计一款赛事管理系统,实现赛务相关的数据管理及信息服务,该系统能够为省级赛事管理解决以下问题:

(1)能够管理各参赛队的基本信息(包含参赛队编号,参赛作品名称,参赛学校,赛事类别,参赛者,指导老师),赛事类别共11项(参见大赛官网jsjds.blcu.edu.cn);包括增加、删除、修改参赛队伍的信息。

(2)从team.txt中读取参赛队伍的基本信息,实现基于二叉排序树的查找。根据提示输入参赛队编号,若查找成功,输出该赛事类别对应的基本信息(参赛作品名称、参赛学校、赛事类别、参赛者和指导老师信息),同时,输出查找成功时的平均查找长度ASL;否则,输出“查找失败!”。
(3)能够提供按参赛学校查询参赛团队(或根据赛事类别查询参赛团队),即,根据提示输入参赛学校名称(赛事类别),若查找成功,输出该学校参赛的(该赛事类别的)所有团队的基本信息,输出的参赛团队按队伍编号有序输出。(排序算法可从选择排序、插入排序、希尔排序、归并排序、堆排序中任意选择,并为选择算法的原因做出说明。)

(4)为省赛现场设计一个决赛叫号系统。所有参赛队按赛事组织文件中的赛事类别分到9个决赛室,决赛室按顺序叫号,被叫号参赛队进场,比赛结束后,下一参赛队才能进赛场。请模拟决赛叫号系统,演示省赛现场各决赛室的参赛队进场情况。(模拟时,要能直观展示叫号顺序与进场秩序一致)

(5)赛事系统为参赛者提供赛地的校园导游程序,为参赛者提供各种路径导航的查询服务。以我校长山校区提供比赛场地为例,(请为参赛者提供不少于10个目标地的导航。可为参赛者提供校园地图中任意目标地(建筑物)相关信息的查询;提供任意两个目标地(建筑物)的导航查询,即查询任意两个目的地(建筑物)之间的一条最短路径。

【设计要求】

1)赛事数据要求存入文件(txt或excel)并能读入查询;

2)赛地目的地查询,需提供目的地(建筑物)名称、代号、简介、两地之间路径长度等信息;

3)输入数据形式和范围:赛事相关数据可从键盘输入,或自文件导入。

4)界面要求:交互设计要合理,每个功能可以设计菜单,用户根据提示,完成相关功能的要求。

二、问题分析

        问题(1)(2): 能够管理各参赛队的基本信息(包含参赛队编号,参赛作品名称,参赛学校,赛事类别,参赛者,指导老师),赛事类别共11项(参见大赛官网jsjds.blcu.edu.cn);包括增加、删除、修改参赛队伍的信息。从team.txt中读取参赛队伍的基本信息,实现基于二叉排序树的查找。根据提示输入参赛队编号,若查找成功,输出该赛事类别对应的基本信息(参赛作品名称、参赛学校、赛事类别、参赛者和指导老师信息),同时,输出查找成功时的平均查找长度ASL;否则,输出“查找失败!”。

        分析:能够管理参赛队基本信息,包括增加、删除、修改参赛队伍的信息。显而易见,我们需要定义一个team结构体类型对参赛队基本信息进行储存从而便于管理,在此基础上需要添加包括增加、删除、修改参赛队伍的信息的函数以实现这些功能。同时,我们需要定义一个赛事管理系统的类,并将这些变量与函数写进赛事管理类中,便于实现与调用,同时实现高聚合。为了实现从team.txt中读取信息,需要包含<fstream>头文件进行相关读取操作。对于基于二叉排序树的查找,需要定义节点结构,并写出相关操作如插入节点,删除节点,查找等。

        问题(3)(4):能够提供按参赛学校查询参赛团队(或根据赛事类别查询参赛团队),即,根据提示输入参赛学校名称(赛事类别),若查找成功,输出该学校参赛的(该赛事类别的)所有团队的基本信息,输出的参赛团队按队伍编号有序输出。(排序算法可从选择排序、插入排序、希尔排序、归并排序、堆排序中任意选择,并为选择算法的原因做出说明。);为省赛现场设计一个决赛叫号系统。所有参赛队按赛事组织文件中的赛事类别分到N个决赛室,决赛室按顺序叫号,被叫号参赛队进场,比赛结束后,下一参赛队才能进赛场。请模拟决赛叫号系统,演示省赛现场各决赛室的参赛队进场情况。(模拟时,要能直观展示叫号顺序与进场秩序一致)。

        分析:为了便于实现按赛事类别查找队伍,我们需要定义一个findTeamsBySchool函数,集中实现这一功能。在解决按队伍编号查找参赛队伍时,我们需要定义unordered_map类型的teamMap作为参赛队伍映射,便于我们进行按队伍编号查找。同样的,在实现按参赛学校名称查找参赛团队时,我们同样定义了一个unordered_map类型的变量schoolMap作为学校映射。需要注意的是,每当我们执行一次添加、删除、修改队伍信息的操作时,不要忘记修改对应schoolMap、teamMap、二叉树排序树里的信息,实现信息同步。在输出参赛团队时需要按赛事类别进行有序输出,就需要我们用某种排序方法对参赛队伍进行排序。在这里,我们选用归并排序作为排序方法,用递归的方式实现。选用归并排序的原因如下:

•  归并排序的效率达到了巅峰:时间复杂度为O (nlogn),这是基于比较的排序算法所能达到的最高境界
•  归并排序是一种稳定的算法(即在排序过程中大小相同的元素能够保持排序前的顺序,3212升序排序结果是1223,排序前后两个2的顺序不变),这一点在某些场景下至关重要。

        为省赛现场设计决赛叫号系统,同样需要定义一个相应函数simulateFinalCallingSystem实现此功能。由于按赛事类别分到决赛室,因此有多少个赛事类别,我们就需要设置多少决赛室。在实现决赛系统叫号时,为尽可能模拟现实情况,应实现多个决赛室并发进行叫号,若为简化设计,可以简单的实现顺序叫号:即按决赛室顺序,一个决赛室叫号完全结束才能进行下一个决赛室的叫号。同时,为演示省赛现场各决赛室的参赛队进场情况,需要把叫号过程打印出来。

        问题(5):赛事系统为参赛者提供赛地的校园导游程序,为参赛者提供各种路径导航的查询服务。以我校长山校区提供比赛场地为例,(请为参赛者提供不少于10个目标地的导航。可为参赛者提供校园地图中任意目标地(建筑物)相关信息的查询;提供任意两个目标地(建筑物)的导航查询,即查询任意两个目的地(建筑物)之间的一条最短路径。

        分析:为了充分表示各目的地(建筑物)的信息与距离关系,我们需要重新定义两个数据结构,其中一个用来表示目的地(建筑物)的具体信息,另外一个用来表示边结构,用于表示两点的边关系。还需要定义一个图的类,用来储存图的相关变量与操作,如所有目的地(建筑物)的信息,目的地(建筑物)之间的关系(邻接表或邻接矩阵),导航功能等,便于复用。最后需要将校园地图的目的地先导入到该类中,便于后续操作。

三、概要设计

        数据结构设计

        参赛队伍结构:首先定义了一个结构体的数据结构Team——用来储存参赛队伍的信息

       struct Team {
    string team_id;           // 参赛队编号
    string project_name;      // 参赛作品名称
    string school;            // 参赛学校
    string category;             // 赛事类别
    string participants;  // 参赛者
    string guide_teacher;     // 指导老师
};

        在Team结构中,分别用team_id,project_name,school,category,participants,guide_teacher表示参赛队编号,参赛作品名称,参赛学校,赛事类别,参赛者,指导老师。

        赛事管理系统结构——之后定义一个赛事管理类,用于管理参赛队伍并对其进行相应操作。

// 定义比赛管理系统类
class CompetitionManagementSystem {
private:
    vector<Team> teams;                          // 参赛队伍列表
    unordered_map<string, Team> teamMap;          // 参赛队伍映射,以队伍编号为键值
    unordered_map<string, vector<Team>> schoolMap; // 学校映射,以学校名称为键值
    Node* root = nullptr;//二叉排序树根节点
    class CampusMap map;
public:
    //打印参赛队伍信息
    void print_team(const Team& team){}
    
    // 添加参赛队伍
    void addTeam(Team&team){}
   
    // 根据队伍编号查找参赛队伍
       
    Team findTeamByID(const string&teamID){}
   
    //修改参赛队伍信息
    void change_team(){}
    
        
    // 根据学校名称查找参赛队伍
    void findTeamsBySchool() {}
     };

        为实现二叉排序树的查找,需要定义节点结构以构建二叉树。

//定义树节点
struct Node {
    int data;
    Node* left;
    Node* right;

    Node(int value) {
        data = value;
        left = nullptr;
        right = nullptr;
    }
};

        为了便于表示校园地图中各目的地(建筑物)的信息,目的地之间的关系(边),我们需要定义两个数据结构 Building和Edge以充分表示。

// 建筑物结构体
struct Building {
    string name;    //建筑物名字
    int code;   //建筑物编号
    string description; //建筑物信息描述
};

// 边结构体
struct Edge {
    int destination;  // 目的地建筑物的索引
    int distance;     // 路径长度
};

        在Building中name,code,description分别表示建筑的名字,编号和信息描述,Edg中destination,distance分别表示目的地的索引和到目的地的路径长度。

        定义一个图的类,用来储存图的相关变量与操作,如所有目的地(建筑物)的信息,目的地(建筑物)之间的关系(邻接表或邻接矩阵),导航功能等,便于复用。

class CampusMap {
private:
    std::vector<Building> buildings;                      // 存储建筑物信息
    std::unordered_map<std::string, int> buildingIndices;  // 建筑物名称到索引的映射
    std::vector<std::vector<Edge>> adjacencyList;          // 邻接表表示的图

public:
    //构造函数
    CampusMap() {}

    // 添加建筑物
    void addBuilding(const Building& building) {}    

    // 添加路径
    void addPath(const string& source, const string& destination, int distance{}

    // 查询建筑物信息
    void queryBuilding(const string& buildingName) {}

    // 查询路径导航
    void queryPath(const string& source, const string& destination) {}
 
    // 递归打印最短路径
    void printPath(const std::vector<int>&previous, int destination) {}
    };

        函数方法与成员变量设计

      为了充分表示各比赛队伍,定义了vector容器teams用于存放Team类型的参赛队伍,用unordered_map类型的teamMap作为参赛队伍映射,以队伍编号为键值便于以队伍编号查找队伍信息,用unordered_map类型的变量schoolMap作为学校映射,用学校为键值便于以学校名称查找队伍,同时定义二叉排序树根节点,初始时用空指针进行初始化。再次强调,每当我们执行一次添加、删除、修改队伍信息的操作时,不要忘记修改对应schoolMap、teamMap、二叉树排序树里的信息,实现信息同步。

private:
    vector<Team> teams;                          // 参赛队伍列表
    unordered_map<string, Team> teamMap;         // 参赛队伍映射,以队伍编号为键值
    unordered_map<string, vector<Team>> schoolMap; // 学校映射,以学校名称为键值
    Node* root = nullptr;//二叉排序树根节点

        题设中要求从.txt文件直接读取赛事信息,所以需要包含 fstream和sstream头文件。

        观察.txt文件中队伍信息的格式,我们会发现第一行是各数据项的标题,如参赛队编号,参赛作品名称等。从第二行往后就是各队伍的信息。对其中每一行进行分析,每个队伍信息中的一小项都用#号进行分隔。因此当我们在进行文件读取的时候,可以从第二行开始一行一行读取,对每一行再进行另外的处理——创建Team类型对象team,对每一行内容,用#当作分隔符,按照参赛队编号、参赛作品名称、参赛学校、赛事类别、参赛者、指导老师的顺序分别赋值给team_id,project_name,school,category,participants,guide_teacher.

        相关代码如下:

 CompetitionManagementSystem cms;

    // 从team.txt中读取参赛队伍的基本信息并添加到系统中
    ifstream file("team.txt");
    if (file.is_open()) {
        string line;
        getline(file, line);//跳过第一行
        while (getline(file, line)) {
            Team team;
            stringstream ss(line);
            string temp;
            // 解析并设置team的各项属性
            getline(ss, team.team_id, '#');
            removeSpaces(team.team_id);
            getline(ss, team.project_name, '#');
            removeSpaces(team.project_name);
            getline(ss, team.school, '#');
            removeSpaces(team.school);
            getline(ss, team.category, '#');
            removeSpaces(team.category);
            getline(ss, team.participants, '#');
            removeSpaces(team.participants);
            getline(ss, team.guide_teacher, '#');
            removeSpaces(team.guide_teacher);
            
            cms.addTeam(team);
            
      
        }
        
        file.close();
    }
    else {
        cout << "无法打开team.txt文件" << endl;
       
    }

        为实现赛事管理系统的基本功能,如打印、增加、删除、修改参赛队伍的信息、查找等,需要对赛事系统添加各种成员函数以实现相关功能。

//打印参赛队伍信息
void print_team(const Team& team)
// 添加参赛队伍
void addTeam(const Team& team)
// 根据队伍编号查找参赛队伍
Team findTeamByID(const string& teamID)
//删除参赛队伍
void delete_team() 
//修改参赛队伍信息
void change_team()
//模拟决赛叫号系统
void simulateFinalCallingSystem()

        在实现查找功能时,要实现基于二叉排序树的查找功能,同样需要定义相关函数进行操作。 其中finMin函数会在remove中用到。在查找成功后需要计算输出查找成功的ASL,为此需要再定义三个函数体,分别用于计算二叉排序树的总结点数,总搜索路径长,和平均搜索路径长(ASL)。

//建立二叉排序树
Node* insertNode(Node* root, int value)
//基于二叉排序树的查找
Node* searchNode(Node* root, int value)
//找到以root为根节点的树最小节点
Node* findMin(Node* root)
//二叉排序树删除节点
Node* remove(Node* root, int key)
// 计算二叉排序树的节点总数
int countNodes(Node* root){}
// 计算二叉排序树的总搜索路径长度
int totalSearchLength(Node* root, int level) {}
// 计算二叉排序树的平均搜索长度
double calculateASL(Node* root) {}

       

        在模拟叫号系统simulateFinalCallingSystem()中,我们定义了一个unordered_map类型的变量finalRooms,该map以赛事类别为键,值为一个队列,用来储存以该键为赛事类别的队伍。一开始各队伍按顺序入队,每叫完一个号时,该队出队。

unordered_map<string,queue<Team>> finalRooms;  // 各决赛室队列

        在定义的CampusMap类中,我们定义了三个成员变量:buildings,buildingIndices,adjacencyList。其中buildings为一个Building类型的vector,用来储存所有建筑,buildingIndices为一个以建筑名称为键的map,其值为该建筑的索引,实现建筑名称到索引的映射。最后adjacencyList是一个以Edge为类型的容器为元素的容器,即容器套容器,用来表示邻接表。

    std::vector<Building> buildings;                      // 存储建筑物信息
    std::unordered_map<std::string, int> buildingIndices;  // 建筑物名称到索引的映射
    std::vector<std::vector<Edge>> adjacencyList;          // 邻接表表示的图

        在CampusMap类中,我们定义了以下函数:

public:
    //构造函数
    CampusMap() {}

    // 添加建筑物
    void addBuilding(const Building& building) {}    

    // 添加路径
    void addPath(const string& source, const string& destination, int distance{}

    // 查询建筑物信息
    void queryBuilding(const string& buildingName) {}

    // 查询路径导航
    void queryPath(const string& source, const string& destination) {}
 
    // 递归打印最短路径
    void printPath(const std::vector<int>&previous, int destination) {}
    };

        其中CampusMap()为构造函数,用来将校园地图导入该类中,并进行相关初始化操作。addBuilding()为添加建筑物函数,在构造函数中通过此函数将建筑物添加进CampusMap类中。addPath()可以添加建筑物之间的路径,用来更新邻接表。

        在本项目中,建筑物及其边关系如下图:

       queryBuilding()用来查询建筑物的详细信息,即输出建筑物的名字、索引(代号)、介绍。queryPath()用来查询到达目的地的最短路径,并输出行走路线。

        在queryPath(),对于最短路径的查找问题,根据数据结构课程所学知识,有多种经典算法可以解决最短路径问题,包括 Dijkstra算法,Floyd-Warshell 算法,Bellman-Ford 算法和深度优先遍历等。其中 Dijkstra 算法求的是单源最短路径:即从一个结点出发到其它所有结点的最短路径,
算法的时间复杂度为 O(n 2 ),但题目要求任意两个结点的最短路径,所以还是要在外层增加一个循环,以求得多源最短路径。而 Floyd 算法求的是多源最短路径:即从任意结点出发到其它所有结点的最短路径,算法的时间复杂度为 O(n 3 ),它可以一次性求得所有结点间的最短路径,且算法思想简单,便于理解。所以我们选用 Floyd 算法来求解最短路径。 为了实现这一算法,我们定义两个容器distance,previous分别用于储存路径长度和存储最短路径上的前驱节点。

std::vector<int> distance(buildings.size(), INT_MAX);  // 存储路径长度
std::vector<int> previous(buildings.size(), -1);       // 存储最短路径上的前驱节点

         系统页面设置

        在完成系统功能的大体结构设计后,最后对系统页面界面进行设计。要求系统界面能包含整个系统的完整功能,并简洁高效,便于操作。基于以上要求,对界面设计如下:

 界面设计相关代码如下:

        
void display_menu() {
    cout << "========== 计算机设计大赛赛事管理系统 ==========" << endl;
    cout << "1. 增加队伍" << endl;
    cout << "2. 删除队伍" << endl;
    cout << "3. 更改队伍信息" << endl;
    cout << "4. 按参赛队伍ID查找" << endl;
    cout << "5. 按参赛学校名称查找" << endl;
    cout << "6. 决赛室叫号" << endl;
    cout << "7. 校园导航" << endl;
    cout << "0. 退出系统" << endl;
    cout << "================================================" << endl;
}

   四、总结

        本次课设是为某一赛事设计一个参赛系统,包括参赛队的管理和各项功能实现。通过本次课程设计,我们巩固了数据结构相关知识,并学习到很多额外知识:

  1. 数据结构的应用:课设涉及了多种数据结构的应用,如二叉排序树、排序算法等。学生可以深入理解和掌握这些数据结构的原理和实现方式,并学会将其应用于实际问题的解决中。

  2. 数据管理和操作:课设要求学生设计参赛队伍的基本信息管理系统,包括增加、删除和修改参赛队伍信息等操作。通过实践,学生可以学会如何有效地组织和管理数据,并实现相应的操作功能。

  3. 查找算法和效率分析:通过基于二叉排序树的参赛队伍查找功能,学生可以学习查找算法的实现原理和应用场景,并了解如何通过效率分析来评估算法的性能,以便进行算法选择和优化。

  4. 排序算法的比较与选择:课设要求学生为参赛团队查询功能选择排序算法,并说明选择的原因。学生需要了解不同排序算法的特点、时间复杂度等,并能根据实际需求选择适合的算法。

  5. 模拟系统的设计和实现:通过决赛叫号系统的设计和模拟,学生可以学会如何设计和实现一个模拟系统,模拟真实场景中的操作和流程,并保证系统的准确性和可视化效果。

  6. 地图导航算法的实现:通过校园导游程序的设计,学生可以学习如何使用图的算法来实现路径导航功能,包括最短路径算法等。这将培养学生在实际问题中应用图算法的能力。

附录

        完整实验代码如下:
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <unordered_map>
#include <queue>
#include <algorithm>
#define INF INT_MAX  // 无穷大
using namespace std;

// 定义参赛队伍结构
struct Team {
    string team_id;           // 参赛队编号
    string project_name;      // 参赛作品名称
    string school;            // 参赛学校
    string category;             // 赛事类别
    string participants;  // 参赛者
    string guide_teacher;     // 指导老师
};
//定义树节点
struct Node {
    int data;
    Node* left;
    Node* right;

    Node(int value) {
        data = value;
        left = nullptr;
        right = nullptr;
    }
};
// 建筑物结构体
struct Building {
    string name;    //建筑物名字
    int code;   //建筑物编号
    string description; //建筑物信息描述
};

// 边结构体
struct Edge {
    int destination;  // 目的地建筑物的索引
    int distance;     // 路径长度
};
//建立二叉排序树
Node* insertNode(Node* root, int value) {
    if (root == nullptr) {
        return new Node(value);
    }

    if (value < root->data) {
        root->left = insertNode(root->left, value);
    }
    else if (value > root->data) {
        root->right = insertNode(root->right, value);
    }

    return root;
}
//基于二叉排序树的查找
Node* searchNode(Node* root, int value) {
    if (root == nullptr || root->data == value) {
        return root;
    }

    if (value < root->data) {
        return searchNode(root->left, value);
    }
    else {
        return searchNode(root->right, value);
    }
}
//找到以root为根节点的树最小节点
Node* findMin(Node* root) {
    if (root == nullptr) {
        return nullptr;
    }

    while (root->left != nullptr) {
        root = root->left;
    }

    return root;
}
//二叉排序树删除节点
Node* remove(Node* root, int key) {
    if (root == nullptr) {
        return nullptr;
    }

    if (key < root->data) {
        root->left = remove(root->left, key);
    }
    else if (key > root->data) {
        root->right = remove(root->right, key);
    }
    else {
        // 找到要删除的节点

        if (root->left == nullptr) {
            Node* temp = root->right;
            delete root;
            return temp;
        }
        else if (root->right == nullptr) {
            Node* temp = root->left;
            delete root;
            return temp;
        }
        else {
            // 当前节点有两个子节点
            Node* minRight = findMin(root->right);
            root->data = minRight->data;
            root->right = remove(root->right, minRight->data);
        }
    }

    return root;
}

// 计算二叉排序树的节点总数
int countNodes(Node* root) {
    if (root == nullptr) {
        return 0;
    }

    return 1 + countNodes(root->left) + countNodes(root->right);
}

// 计算二叉排序树的总搜索路径长度
int totalSearchLength(Node* root, int level) {
    if (root == nullptr) {
        return 0;
    }

    return level + totalSearchLength(root->left, level + 1) + totalSearchLength(root->right, level + 1);
}
// 计算二叉排序树的平均搜索长度
double calculateASL(Node* root) {
    int nodeCount = countNodes(root);
    int totalLength = totalSearchLength(root, 1);

    if (nodeCount == 0) {
        return 0.0;
    }

    return double(totalLength) / nodeCount;
}
//去除字符串的空格
void removeSpaces(string& input) {
    string result;
    for (char c : input) {
        if (c != '\t') {
            result += c;
        }
    }
    input = result;
}
// 归并排序,按照编号对团队进行排序
void mergeSort(std::vector<Team>& teams) {
    if (teams.size() <= 1) {
        return;
    }
    int mid = teams.size() / 2;
    std::vector<Team> left(teams.begin(), teams.begin() + mid);
    std::vector<Team> right(teams.begin() + mid, teams.end());

    mergeSort(left);
    mergeSort(right);

    int i = 0, j = 0, k = 0;
    while (i < left.size() && j < right.size()) {
        if (left[i].team_id < right[j].team_id){
            teams[k++] = left[i++];
        }
        else {
            teams[k++] = right[j++];
        }
    }

    while (i < left.size()) {
        teams[k++] = left[i++];
    }

    while (j < right.size()) {
        teams[k++] = right[j++];
    }
}
// 图类
class CampusMap {
private:
    std::vector<Building> buildings;                      // 存储建筑物信息
    std::unordered_map<std::string, int> buildingIndices;  // 建筑物名称到索引的映射
    std::vector<std::vector<Edge>> adjacencyList;          // 邻接表表示的图

public:
    CampusMap() {
        Building building[12];
        building[0] = { "行政楼",0,"行政研发中心,层高13层,用于日常办公。" };
        building[1] = { "海韵湖",1,"位于北门附近,呈鸡爪型,在这里你可以看到天鹅。" };
        building[2] = { "图书馆",2,"层高6层,具有智能借还书机器,十分智能。" };
        building[3] = { "东苑食堂",3,"层高3层,蚕桑丝绸主题餐厅,环境具有蚕桑丝绸特色,菜品丰富。" };
        building[4] = { "东操场",4,"内含足球场和各种锻炼器材,用于师生锻炼放松。" };
        building[5] = { "南门",5,"校门进出口,需要扫脸进出。" };
        building[6] = { "体育中心",6,"体育运动中心,内有室内篮球馆,足球馆,羽毛球馆,健美操馆等" };
        building[7] = { "西操场",7,"内含足球场和各种锻炼器材,用于师生锻炼放松。" };
        building[8] = { "经世楼",8,"层高5层,用于日常教学办公,取自校训“笃学明德,经世致用”" };
        building[9] = { "文理大楼",9,"层高23层,造型独特,是江科大标志性建筑,用于日常教学和办公。" };
        building[10] = { "西苑食堂",10,"层高3层,船海主题餐厅,环境具有船海特色,菜品丰富。" };
        building[11] = { "西宿舍区",11,"西区学生住宿区,每间宿舍为四人间,有独立卫浴和阳台。" };

        for (int i = 0; i < 12; i++) {
            addBuilding(building[i]);
        }


        addPath("行政楼", "海韵湖", 300);
        addPath("行政楼", "文理大楼", 700);
        addPath("海韵湖", "行政楼", 300);
        addPath("海韵湖", "图书馆", 600);
        addPath("图书馆", "海韵湖", 600);
        addPath("图书馆", "文理大楼", 400);
        addPath("图书馆", "西操场", 550);
        addPath("图书馆", "东苑食堂", 100);
        addPath("东苑食堂", "图书馆", 100);
        addPath("东苑食堂", "体育中心", 550);
        addPath("东苑食堂", "东操场", 100);
        addPath("东操场", "东苑食堂", 100);
        addPath("东操场", "南门", 250);
        addPath("南门", "东操场", 250);
        addPath("南门", "体育中心", 250);
        addPath("南门", "西宿舍区", 700);
        addPath("体育中心", "南门", 250);
        addPath("体育中心", "东苑食堂", 550);
        addPath("体育中心", "西操场", 100);
        addPath("体育中心", "西苑食堂", 300);
        addPath("西操场", "体育中心", 100);
        addPath("西操场", "图书馆", 550);
        addPath("西操场", "经世楼", 100);
        addPath("西操场", "西苑食堂", 250);
        addPath("经世楼", "西操场", 100);
        addPath("经世楼", "文理大楼", 100);
        addPath("文理大楼", "经世楼", 100);
        addPath("文理大楼", "行政楼", 700);
        addPath("西苑食堂", "西操场", 250);
        addPath("西苑食堂", "体育中心", 250);
        addPath("西苑食堂", "西宿舍区", 200);
        addPath("西宿舍区", "西苑食堂", 200);
        addPath("西宿舍区", "南门", 700);




    }
    // 添加建筑物
    void addBuilding(const Building& building) {
        int index = buildings.size();
        buildings.push_back(building);
        buildingIndices[building.name] = index;
        adjacencyList.emplace_back();
    }

    // 添加路径
    void addPath(const std::string& source, const std::string& destination, int distance) {
        int sourceIndex = buildingIndices[source];
        int destinationIndex = buildingIndices[destination];
        adjacencyList[sourceIndex].push_back({ destinationIndex, distance });
        adjacencyList[destinationIndex].push_back({ sourceIndex, distance });
    }

    // 查询建筑物信息
    void queryBuilding(const std::string& buildingName) {
        if (buildingIndices.count(buildingName) > 0) {
            int index = buildingIndices[buildingName];
            Building building = buildings[index];
            std::cout << "名称: " << building.name << std::endl;
            std::cout << "代号: " << building.code << std::endl;
            std::cout << "简介: " << building.description << std::endl;
        }
        else {
            std::cout << "未找到该建筑物信息。" << std::endl;
        }
    }

    // 查询路径导航(使用Floyd算法)
    void queryPath(const std::string& source, const std::string& destination) {
        if (buildingIndices.count(source) > 0 && buildingIndices.count(destination) > 0) {
            int sourceIndex = buildingIndices[source];
            int destinationIndex = buildingIndices[destination];

            int numBuildings = buildings.size();
            std::vector<std::vector<int>> distance(numBuildings, std::vector<int>(numBuildings, INT_MAX));
            std::vector<std::vector<int>> next(numBuildings, std::vector<int>(numBuildings, -1));

            // 初始化距离矩阵和下一个节点矩阵
            for (int i = 0; i < numBuildings; ++i) {
                distance[i][i] = 0;
                for (const Edge& edge : adjacencyList[i]) {
                    distance[i][edge.destination] = edge.distance;
                    next[i][edge.destination] = edge.destination;
                }
            }

            // Floyd算法计算最短路径和下一个节点
            for (int k = 0; k < numBuildings; ++k) {
                for (int i = 0; i < numBuildings; ++i) {
                    for (int j = 0; j < numBuildings; ++j) {
                        if (distance[i][k] != INT_MAX && distance[k][j] != INT_MAX && distance[i][k] + distance[k][j] < distance[i][j]) {
                            distance[i][j] = distance[i][k] + distance[k][j];
                            next[i][j] = next[i][k];
                        }
                    }
                }
            }
            queryBuilding(destination);
            // 输出最短路径和长度
            if (distance[sourceIndex][destinationIndex] != INT_MAX) {
                std::cout << "最短路径长度: " << distance[sourceIndex][destinationIndex] << std::endl;
                std::cout << "路径导航: ";
                printFloydPath(next, sourceIndex, destinationIndex);
                cout << endl;
            }
            else {
                std::cout << "无法找到从 " << source << " 到 " << destination << " 的路径。" << std::endl;
            }
        }
        else {
            std::cout << "建筑物信息不完整。" << std::endl;
        }
    }

    // 打印Floyd算法计算的最短路径
    void printFloydPath(const std::vector<std::vector<int>>& next, int source, int destination) {
        if (source == destination) {
            std::cout << buildings[source].name;
        }
        else if (next[source][destination] == -1) {
            std::cout << "无法找到路径。";
        }
        else {
            std::cout << buildings[source].name << " -> ";
            printFloydPath(next, next[source][destination], destination);
        }
    }

    };

// 定义比赛管理系统类
class CompetitionManagementSystem {
private:
    vector<Team> teams;                          // 参赛队伍列表
    unordered_map<string, Team> teamMap;          // 参赛队伍映射,以队伍编号为键值
    unordered_map<string, vector<Team>> schoolMap; // 学校映射,以学校名称为键值
    Node* root = nullptr;//二叉排序树根节点
public:
    class CampusMap map;
public:
    //打印参赛队伍信息
    void print_team(const Team& team)
    {
        cout << "队伍编号:" << team.team_id << endl << "作品名称:" << team.project_name << endl << "参赛学校:" << team.school << endl
            << "赛事类别:" << team.category << endl << "参赛者:" << team.participants << endl << "指导老师:" << team.guide_teacher << endl;
        cout << endl;
    }
    // 添加参赛队伍
    void addTeam()
    {
        bool conti = true;
        while (conti) {
            Team team;
            cout << "请输入队伍信息" << endl;
            cout << "队伍编号:" << endl;
            cin >> team.team_id;
            cout << "参赛作品名称:" << endl;
            cin >> team.project_name;
            cout << "参赛学校:" << endl;
            cin >> team.school;
            cout << "赛事类别:" << endl;
            cin >> team.category;
            cout << "参赛者:" << endl;
            cin >> team.participants;
            cout << "指导老师:" << endl;
            cin >> team.guide_teacher;
            teams.push_back(team);
            teamMap[team.team_id] = team;
            this->root = insertNode(root, atoi(team.team_id.c_str()));
            schoolMap[team.school].push_back(team);

            // 将队伍写入txt文件
            std::ofstream file("team.txt", std::ios::app);
            if (file.is_open()) {
                file << team.team_id << "\t" << '#' << '\t' << team.project_name << "\t" << '#' << '\t' << team.school << "\t" << '#'
                    << '\t' << team.category << "\t" << '#' << '\t' << team.participants << "\t" << '#' << '\t' << team.guide_teacher;
                file << "\n";
                file.close();
            }
            else {
                // 文件打开失败
                std::cout << "文件打开失败" << std::endl;
            }
            cout << "是否继续添加?(Y/N)" << endl;
            char select;
            cin >> select;
            if (select != 'Y')
                conti = false;
            else if (select == 'Y')
                conti = true;
        }
           
    }
    void addTeam(Team& team,int flag)
    {
        teams.push_back(team);
        teamMap[team.team_id] = team;
        this->root = insertNode(root, atoi(team.team_id.c_str()));
        schoolMap[team.school].push_back(team);

    }
    // 根据队伍编号查找参赛队伍
    Team findTeamByID(const string&teamID,bool ISNOT_print)
    {
        double ASL;
        Node* result = searchNode(this->root, atoi(teamID.c_str()));
        //查找成功
        if (result != nullptr) {
            //cout << "查找成功!" << endl;
            print_team(teamMap[teamID]);
            if (!ISNOT_print) {
                double ASL = calculateASL(root);
                cout << "ASL为:" << ASL << endl;
            }
            return teamMap[teamID];

        }
        //查找失败
        cout << "查找失败!" << endl;
        return Team();  // 返回空的Team结构表示未找到
    }
    //删除参赛队伍
    void delete_team() 
    {
        string teamID;
        bool conti = true;
        while (conti) {
            cout << "请输入要删除队伍编号:" << endl;
            cin >> teamID;
            //查找成功
            if (findTeamByID(teamID,true).team_id != " ") {
                vector<Team>::iterator it;
                unordered_map<string, Team>::iterator iter;
                for (it = teams.begin(); it != teams.end(); ) {
                    if (it->team_id == teamID) {
                        //删除schoolMap中对应元素
                        Team teamToDelete = *it;
                        auto ite = schoolMap.find(it->school);
                        if (ite != schoolMap.end()) {
                            std::vector<Team>& teamVector = ite->second;

                            // 遍历vector<Team>查找并删除指定的元素
                            for (auto iter2 = teamVector.begin(); iter2 != teamVector.end(); ++iter) {
                                if (iter2->team_id == teamToDelete.team_id) {
                                    teamVector.erase(iter2);
                                    break; // 找到并删除元素后,可以提前退出循环
                                }
                            }
                        }
                        //删除teams中对应元素
                        it = teams.erase(it);
                        //删除teamMap中对应元素
                        iter = teamMap.find(teamID);
                        iter = teamMap.erase(iter);
                        //删除二叉排序树中对应元素
                        remove(this->root, atoi(teamID.c_str()));
                        deleteTeamInfo(teamID);
                        cout << "删除成功!" << std::endl;
                    }
                    else {
                        ++it;
                    }
                }
                cout << "是否继续删除?(Y/N)" << endl;
                char select;
                cin >> select;
                if (select != 'Y')
                    conti = false;
                else if (select == 'Y')
                    conti = true;
            }
            //查找失败
            else
                cout << "输入错误,请重试!" << endl;
        }   
        
    }
    void deleteTeamInfo( string& teamID)
    {
        std::ifstream file("team.txt");
        std::vector<Team> teams;

        // 读取原始文件内容到vector中
        if (file.is_open()) {
            std::string line;
            while (std::getline(file, line)) {
                std::istringstream iss(line);
                Team team;
                std::getline(iss, team.team_id, '#');
                std::getline(iss, team.project_name, '#');
                std::getline(iss, team.school, '#');
                std::getline(iss, team.category, '#');
                std::getline(iss, team.participants, '#');
                std::getline(iss, team.guide_teacher, '#');
                removeSpaces(team.team_id);
                if (team.team_id != teamID) {
                    teams.push_back(team);
                }
            }
            file.close();
        }
        else {
            std::cout << "打开文件失败!" << std::endl;
            return;
        }

        // 将更新后的队伍信息写回文件
        std::ofstream outfile("team.txt");
        if (outfile.is_open()) {
            for (const auto& team : teams) {
                outfile << team.team_id << '#' << team.project_name << '#' << team.school << '#'
                    << team.category << '#' << team.participants << '#' << team.guide_teacher << '\n';
            }
            outfile.close();
            
        }
        else {
            std::cout << "重新写入失败!" << std::endl;
        }
    }
    //修改参赛队伍信息
    void modifyTeamInfo()
    {
        string teamID;
        
        bool conti = true;
        while (conti) {
            // 查找队伍是否存在
            cout << "请输入要修改队伍编号:" << endl;
            cin >> teamID;
            if (teamMap.find(teamID) != teamMap.end()) {
                Team& team = teamMap[teamID];
                print_team(team); // 打印当前队伍信息

                // 提示用户输入新的队伍信息
                cout << "请输入新的参赛作品名称:";
                cin >> team.project_name;
                cout << "请输入新的参赛学校:";
                cin >> team.school;
                cout << "请输入新的赛事类别:";
                cin >> team.category;
                cout << "请输入新的参赛者:";
                cin >> team.participants;
                cout << "请输入新的指导老师:";
                cin >> team.guide_teacher;

                // 更新学校映射中的队伍信息
                vector<Team>& schoolTeams = schoolMap[team.school];
                for (auto& t : schoolTeams) {
                    if (t.team_id == teamID) {
                        t = team;
                        break;
                    }
                }

                // 更新vector<Team> teams中的队伍信息
                for (auto& t : teams) {
                    if (t.team_id == teamID) {
                        t = team;
                        break;
                    }
                }
                std::ifstream inputFile("team.txt");
                std::string firstLine;
                if (inputFile.is_open()) {
                    std::getline(inputFile, firstLine);
                    inputFile.close();
                }
                else {
                    // 文件打开失败的处理逻辑
                    std::cout << "文件打开失败!" << std::endl;
                    return;
                }
                // 更新txt文件
                std::ofstream file("team.txt");
                if (file.is_open()) {
                    file << firstLine << std::endl; // 写入第一行文字
                    for (const auto& t : teams) {
                        file << t.team_id << "\t" << '#' << '\t' << t.project_name << "\t" << '#' << '\t' << t.school << "\t" << '#'
                            << '\t' << t.category << "\t" << '#' << '\t' << t.participants << "\t" << '#' << '\t' << t.guide_teacher;
                        file << "\n";
                    }
                    file.close();
                }
                else {
                    // 文件打开失败的处理逻辑
                    std::cout << "文件打开失败!" << std::endl;
                }

                // 更新二叉排序树
                root = remove(root, atoi(teamID.c_str()));
                root = insertNode(root, atoi(team.team_id.c_str()));

                cout << "队伍信息修改成功!" << endl;
                cout << "是否继续修改?(Y/N)" << endl;
                char select;
                cin >> select;
                if (select == 'N')
                    conti = false;
                else if (select == 'Y')
                    conti = true;
            }
            else {
                cout << "队伍不存在,无法修改信息!" << endl;
            }
        }
    }


        
    // 根据学校名称查找参赛队伍
    void findTeamsBySchool() {
        string schoolName;
        cout << "请输入学校名称:" << endl;
        cin >> schoolName;
        auto it = schoolMap.find(schoolName);
        if (it != schoolMap.end()) {
            std::vector<Team> teams = it->second;

            // 按赛事类别对团队进行排序
            mergeSort(teams);

            // 输出排序后的团队信息
            for (const Team& team : teams) {
                // 输出其他团队基本信息
                print_team(team);
            }
        }
        else {
            cout << "没有找到该学校!" << std::endl;
        }
    }
    

     //模拟决赛叫号系统
    void simulateFinalCallingSystem() {
        unordered_map<string,queue<Team>> finalRooms;  // 各决赛室队列
        //将参赛队按赛事类别分配到决赛室
        for (Team team : teams) {
            finalRooms[team.category].push(team);
        }

         //模拟叫号与进场过程
        for (auto q : finalRooms) {
           
            int i = 1;
            while (!finalRooms[q.first].empty()) {
                cout << q.first << "决赛室 " << i << " 号召集:" << endl;
                string teamID = finalRooms[q.first].front().team_id;
                finalRooms[q.first].pop();
                cout << "队伍 " << teamID << " 进场" << endl;
                cout << "正在进行比赛..." << endl;
                system("pause");
                cout << "队伍 " << teamID << " 比赛结束" << endl;
                i++;
                cout << endl;
            }
           
            cout << endl;
        }
    }


};


void display_menu() {
    cout << "========== 计算机设计大赛赛事管理系统 ==========" << endl;
    cout << "1. 增加队伍" << endl;
    cout << "2. 删除队伍" << endl;
    cout << "3. 更改队伍信息" << endl;
    cout << "4. 按参赛队伍ID查找" << endl;
    cout << "5. 按参赛学校名称查找" << endl;
    cout << "6. 决赛室叫号" << endl;
    cout << "7. 校园导航" << endl;
    cout << "0. 退出系统" << endl;
    cout << "================================================" << endl;
}

int main() {
    CompetitionManagementSystem cms;

    // 从team.txt中读取参赛队伍的基本信息并添加到系统中
    ifstream file("team.txt");
    cout << "导入文件信息....." << endl;
    if (file.is_open()) {
        string line;
        getline(file, line);//跳过第一行
        while (getline(file, line)) {
            Team team;
            stringstream ss(line);
            string temp;
            // 解析并设置team的各项属性
            getline(ss, team.team_id, '#');
            removeSpaces(team.team_id);
            getline(ss, team.project_name, '#');
            removeSpaces(team.project_name);
            getline(ss, team.school, '#');
            removeSpaces(team.school);
            getline(ss, team.category, '#');
            removeSpaces(team.category);
            getline(ss, team.participants, '#');
            removeSpaces(team.participants);
            getline(ss, team.guide_teacher, '#');
            removeSpaces(team.guide_teacher);
            
            cms.addTeam(team,1);
            
      
        }
        
        file.close();
    }
    else {
        cout << "无法打开team.txt文件" << endl;
       
    }
    int choice;

    // 主循环
    while (true) {
        display_menu();
        cout << "请选择操作:";
        cin >> choice;
        cout << endl;

        switch (choice) {
        case 1:
        {
            cout << "执行增加队伍操作......" << endl;
            cms.addTeam();
            cout << "添加成功!" << endl;
            break;
        }
        case 2:
            cout << "执行删除队伍操作......" << endl;
            cms.delete_team();
            break;
        case 3:
            cout << "执行更改队伍信息操作......" << endl;
            cms.modifyTeamInfo();
            break;
        case 4:
        {
            string teamID;
            cout << "执行按参赛队伍ID查找操作......" << endl;
            cout << "请输入队伍编号:" << endl;
            cin >> teamID;
            cms.findTeamByID(teamID,false);
            break;
        }
        case 5:
            cout << "执行按参赛学校名称查找操作......" << endl;
            cms.findTeamsBySchool();
            break;
        case 6:
        {  
            cout << "执行叫号操作......" << endl;
            cms.simulateFinalCallingSystem();
            break;
        }
        case 7:
        {
            string source, destination;
            cout << "执行校园导航操作......" << endl;
            cout << "请输入起始地和目的地" << endl;
            cin >> source >> destination;
            cout << endl;
            cms.map.queryPath(source, destination);
            break;
        }
        case 0:
            cout << "感谢使用,再见!" << endl;
            return 0;
        default:
            cout << "无效的选项,请重新选择!" << endl;
            break;
        }
    }

   
    //cms.findTeamsBySchool("江苏科技大学");
    //cms.map.queryBuilding("西宿舍区");
    //cms.map.queryPath("行政楼", "经世楼");
    
    return 0;
}

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值