赛事管理系统

中国大学生计算机设计大赛省级赛事管理系统

中国大学生计算机设计大赛是我国高校面向本科生的计算机应用设计大赛,大赛旨在激发学生学习计算机知识和技能的兴趣与潜能,提高学生运用信息技术解决实际问题的综合能力。通过大赛这种计算机教学实践形式,可展示师生的教与学成果,最终以赛促学,以赛促教,以赛促创。该赛事在历届学生中影响力较大,参与者众多,请结合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个目标地的导航。可为参赛者提供校园地图中任意目标地(建筑物)相关信息的查询;提供图中任意目标地(建筑物)的问路查询,即查询任意两个目的地(建筑物)之间的一条最短的简单路径。

参赛队伍管理

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

1.1 问题分析

管理各参赛队的基本信息的前提是得先获得各个参赛队的信息,此时需要先进行读取,详细信息见读取信息模块。读取时已经将详细信息按照队伍编号大小存入了二叉排序树中,此时需要对得到的二叉排序树进行相关操作来进行增加、删除、修改的操作,在实现上述功能时,在对节点进行增加、删除、修改后,需要在txt文件中也进行相关操作,删除节点时应该保持二叉排序树的完好。

1.2算法设计

1、采用结构体定义参赛队伍的结构,包括参赛队编号、参赛作品名称、参赛学校、赛事类别、参赛者、指导教师等成员变量。定义二叉排序树的节点也需要采用结构体来定义。

// 参赛队伍结构体

struct Team {

    string teamNumber;

    string projectName;

    string university;

    string eventCategory;

    string participants;

    string guideTeacher;

};

// 参赛队伍节点结构体

struct TeamNode {

    Team team;

    TeamNode* left;

    TeamNode* right;

    int height = 1;

    TeamNode()

    {

       this->team.teamNumber = team.teamNumber;

       this->team.projectName = team.projectName;

       this->team.eventCategory = team.eventCategory;

       this->team.participants = team.participants;

       this->team.university = team.university;

       this->team.guideTeacher = team.guideTeacher;

      

       left = nullptr;

       right = nullptr;

    }

   

    // 构造函数,接受队伍编号作为参数

    TeamNode(const string& number, const string& projectName,const string& university, const string& eventCategory, const string& participants, const string& guideTeacher) {

       TeamNode* node = new TeamNode();

       team.teamNumber = number;

       team.projectName = projectName;

       team.university = university;

       team.eventCategory = eventCategory;

       team.participants = participants;

       team.guideTeacher = guideTeacher;

       left = nullptr;

       right = nullptr;

    }

2、在对节点进行相关操作时,应该注意二叉树的平衡,适当做出调整。

TeamNode* EventManagementSystem::rotateLeft(TeamNode* node)

{

}

    TeamNode* EventManagementSystem::rotateRight(TeamNode* node)

{

}

1.3 算法实现

//添加队伍信息

void EventManagementSystem::addTeam()

{

    //首先创建新节点,以便存放需要添加的队伍信息

    TeamNode* newNode = new TeamNode;

    // 从用户输入获取参赛队伍信息

    cout << "请输入参赛队伍编号:";

    cin.ignore(); // 忽略之前的换行符

    getline(cin, newNode->team.teamNumber);

    cout << "请输入参赛作品名称:";

    getline(cin, newNode->team.projectName);

    cout << "请输入参赛学校:";

    getline(cin, newNode->team.university);

    cout << "请输入赛事类别:";

    getline(cin, newNode->team.eventCategory);

    cout << "请输入参赛者:";

    getline(cin, newNode->team.participants);

    cout << "请输入指导教师:";

    getline(cin, newNode->team.guideTeacher);

    //txt文件中修改

    ofstream mycout("team.txt", ios::app);

    mycout << newNode->team.teamNumber << "  #   " << newNode->team.projectName << "  #   "  << newNode->team.university << "   #   " << newNode->team.eventCategory << "   #   " << newNode->team.participants << " #   " << newNode->team.guideTeacher << endl;

    mycout.close();

   

    //相关信息录入完成,将当前节点的左右子树分别赋为空

    newNode->left = nullptr;

    newNode->right = nullptr;

    // 插入节点到二叉排序树

    //如果根节点为空,则当前节点就是根节点

    if (root == nullptr) {

        root = newNode;

    }

    //否则将当前节点插入二叉排序树中

    else {

        insertNode(root, newNode);

    }

    //给出添加成功的提示

    cout << "参赛队伍信息已添加。\n";

}

//删除参赛队伍信息

void EventManagementSystem::deleteTeam(string teamNumber)

{

    //如果根节点为空,说明本来就没有参赛队伍信息,无法进行删除

    if (root == nullptr) {

        cout << "当前无参赛队伍信息。\n";

        return;

    }

    // 查找要删除的节点,将查找到的节点赋值给一个新的节点targetNode

    TeamNode* targetNode = searchNode(root, teamNumber);

    //如果找不到要删除的节点,给出提示

    if (targetNode == nullptr) {

        cout << "未找到对应的参赛队伍信息。\n";

        return;

    }

    //找到要删除的节点

    // 执行删除操作

    root = deleteNode(root, teamNumber);

    deletefile(teamNumber);

    cout << "参赛队伍信息已删除。\n";

}

//修改

void EventManagementSystem::modifyTeam()

{

    if (root == nullptr) {

        cout << "当前无参赛队伍信息。\n";

        return;

    }

    string teamNumber;

    cout << "请输入要修改的参赛队伍编号:";

    cin >> teamNumber;

    TeamNode* target = searchNode(root, teamNumber);

    if (target == nullptr) {

        cout << "未找到对应的参赛队伍信息。\n";

        return;

    }

    // 根据需要,修改参赛队伍的字段信息

    cout << "请输入新的参赛作品名称:";

    cin.ignore();

    getline(cin, target->team.projectName);

    cout << "请输入新的参赛学校:";

    getline(cin, target->team.university);

    cout << "请输入新的赛事类别:";

    getline(cin, target->team.eventCategory);

    cout << "请输入新的参赛者:";

    getline(cin, target->team.participants);

    cout << "请输入新的指导教师:";

    getline(cin, target->team.guideTeacher);

    updateInFile(teamNumber, target->team);

    // 输出修改成功的提示信息

cout << "参赛队伍信息已修改并保存到 team.txt 文件中。\n";

}

1.4 实验结果

 增加队伍:根据提示输入指令和需要添加的相关信息,就能在二叉排序树中添加新的节点,并保持二叉树的结构,在文件中也添加该队伍信息。

删除队伍:根据提示输入指令和需要删除的队伍编号,就能在二叉排序树中删除该节点,并保持二叉树的结构,在文件中也删除该队伍信息。

 修改队伍信息:根据提示输入指令和需要修改的相关信息,就能在二叉排序树中找到该节点并修改,在文件中也就进行修改。

2 基于二叉树的查找

team.txt中读取参赛队伍的基本信息,实现基于二叉排序树的查找。根据提示输入参赛队编号,若查找成功,输出该赛事类别对应的基本信息(参赛作品名称、参赛学校、赛事类别、参赛者和指导老师信息),同时,输出查找成功时的平均查找长度ASL;否则,输出查找失败!

2.1 问题分析

使用二叉排序树(BST)来进行查找。具体实现过程如下:

1、从数据文件 team.txt 中读取参赛队伍的基本信息,构建一个二叉排序树。

2、读取用户输入的参赛队伍编号,将其作为关键字进行查找。

3、在二叉排序树中查找该关键字,如果查找成功,则输出该赛事类别对应的基本信息(参赛作品名称、参赛学校、赛事类别、参赛者和指导老师信息),并计算平均查找长度 ASL;否则,输出“查找失败!”。

4、重复步骤2-3,直到所有参赛队伍的基本信息都被查找完毕。

2.2 算法设计

1、采用字符串用于存储和处理队伍基本信息。

//从输入流中读取一行文本,并将其存储到一个字符串变量中

    string line;

2、采用文件流用于读取txt文件。

ifstream inFile("team.txt");//打开文件

inFile.close();//关闭文件

3、采用二叉排序树来存储参赛队伍信息,根据参赛队伍编号(String类型)大小来构建二叉排序树,可以实现参赛队伍信息的添加、删除和修改。

TeamNode* root;//二叉排序树根节点

// 为当前行的信息用队伍编号创建一个新的队伍节点

        TeamNode* newNode = new TeamNode(teamNumber,projectName,university,eventCategory,participants,guideTeacher);

        //如果根节点为空则当前节点就是根节点

        if (root == nullptr)

        {

            root = newNode;

        }

        //如果根节点不空,则将创建的新节点插入到二叉排序树中

        else

        {

            insertNode(root, newNode);

        }

2.3 算法实现

//将赛事信息从team.txt文件中读取出来并放入二叉排序树中

void EventManagementSystem::loadTeamsFromFile()

{

    ifstream inFile("team.txt");//打开文件

    if (!inFile) {

        cout << "无法打开文件 team.txt\n";

        return;

    }

    //从输入流中读取一行文本,并将其存储到一个字符串变量中

    string line;

    getline(inFile, line); // 读取文件中的标题行,忽略

    while (getline(inFile, line)) {

        // 使用 stringstream 进行分割

        stringstream ss(line);

        string temp;

        string teamNumber;

        getline(ss, teamNumber, '\t');

        ss.ignore();

        getline(ss, temp, '\t'); // 跳过参赛作品名称的 #

        string projectName;

        getline(ss, projectName, '\t');

        ss.ignore();

        getline(ss, temp, '\t'); // 跳过参赛学校的 #

        string university;

        getline(ss, university, '\t');

        ss.ignore();

        getline(ss, temp, '\t'); // 跳过赛事类别的 #

        string eventCategory;

        getline(ss, eventCategory, '\t');

        ss.ignore();

        getline(ss, temp, '\t'); // 跳过参赛者的 #

        string participants;

        getline(ss, participants, '\t');

        ss.ignore();

        getline(ss, temp, '\t'); // 跳过指导教师的 #

        string guideTeacher;

        getline(ss, guideTeacher);

      

        // 输出格式化的信息

        cout << "参赛队伍编号: " << teamNumber << endl;

        cout << "参赛作品名称: " << projectName << endl;

        cout << "参赛学校: " << university << endl;

        cout << "赛事类别: " << eventCategory << endl;

        cout << "参赛者: " << participants << endl;

        cout << "指导教师: " << guideTeacher << endl;

      

       // 为当前行的信息用队伍编号创建一个新的队伍节点

        TeamNode* newNode = new TeamNode(teamNumber,projectName,university,eventCategory,participants,guideTeacher);

        //如果根节点为空则当前节点就是根节点

        if (root == nullptr)

        {

            root = newNode;

        }

        //如果根节点不空,则将创建的新节点插入到二叉排序树中

        else

        {

            insertNode(root, newNode);

        }

        cout << endl;

    }

    //读取完毕,关闭文件

    inFile.close();

cout << "数据已从 team.txt 文件读取并输出。\n";

}

//按编号查找队伍并输出ASL

void EventManagementSystem::searchTeam()

{

    int totalPathLength = 0;

    int nodeCount = 0;

    //如果根节点为空,说明本来就没有参赛队伍信息,无法进行查询

    if (root == nullptr) {

        cout << "当前无参赛队伍信息。\n";

        return;

    }

    string teamNumber;

    cout << "请输入要查询的参赛队伍编号:";

    cin >> teamNumber;

    // 查找要查询的节点,将查找到的节点赋值给一个新的节点targetNode

    TeamNode* targetNode = searchNode(root, teamNumber);

    //如果找不到要查询的节点,给出提示

    if (targetNode == nullptr) {

        cout << "未找到对应的参赛队伍信息。\n";

        return;

    }

    // 查找要查询的节点并计算路径长度

    int pathLength = calculatePathLength(root, teamNumber, 1);

    if (pathLength == 0) {

        cout << "未找到对应的参赛队伍信息。\n";

        return;

    }

    totalPathLength += pathLength;

    nodeCount++;

    //找到要查询的节点

    cout << "参赛队伍编号: " << targetNode->team.teamNumber << endl;

    cout << "参赛作品名称: " << targetNode->team.projectName << endl;

    cout <<"参赛学校: " << targetNode->team.university << endl;

    cout << "赛事类别: "<<targetNode->team.eventCategory << endl;

    cout << "参赛者: "<<targetNode->team.participants << endl;

    cout << "指导教师: " <<targetNode->team.guideTeacher << endl;

   

    double asl = static_cast<double>(totalPathLength) / nodeCount;

    cout << "平均查找长度ASL" << asl << endl;

    cout << "ASL的计算公式:" << endl;

    cout << "ASL = (h1 + h2 + ... + hn) / n" << endl;

    cout << "其中,hi 表示查找到的参赛队所在的层级,n 表示节点的总数" << endl;

}

2.4 实验结果

用户根据指令输入需要查找的参赛队伍编号就能得到相关的队伍信息。

3 参赛团队查询

能够提供按参赛学校查询参赛团队(或根据赛事类别查询参赛团队),即,根据提示输入参赛学校名称(赛事类别),若查找成功,输出该学校参赛的(该赛事类别的)所有团队的基本信息,输出的参赛团队按赛事类别有序输出。(排序算法可从选择排序、插入排序、希尔排序、归并排序、堆排序中任意选择,并为选择算法的原因做出说明。)

3.1 问题分析

从数据文件中读取参赛队伍的基本信息,包括参赛队伍编号、参赛作品名称、参赛学校、赛事类别、参赛者和指导老师信息等。提示用户输入参赛学校名称或赛事类别,并进行输入验证和错误提示。根据用户输入的参赛学校名称或赛事类别,在数据文件中查找相应的参赛团队。将查找到的参赛团队按赛事类别进行排序,并输出排序后的参赛团队的基本信息。

3.2 算法设计

采用选择排序算法进行排序,每次从待排序的元素中选择最小(或最大)的元素,将其与未排序部分的第一个元素交换位置,直到所有元素都排序完成。选择排序的时间复杂度是O(n^2),其中n是参赛队伍的数量,每个学校的参赛团队数量较小,选择排序的性能足够满足需求,并且实现简单。

3.3 算法实现

//按学校查找队伍

void EventManagementSystem::searchTeamByUniversity(const string& university, const vector<Team>& teamInfos) {

    vector<Team> matchedTeams;

    // 遍历参赛队伍,找到匹配的队伍信息

    for (const Team& team : teamInfos) {

        if (team.university == university) {

            matchedTeams.push_back(team);

        }

    }

    if (matchedTeams.empty()) {

        cout << "未找到匹配的队伍信息" << endl;

        return;

    }

    // 使用选择排序按参赛队编号排序

    for (int i = 0; i < matchedTeams.size() - 1; i++) {

        int minIndex = i;

        for (int j = i + 1; j < matchedTeams.size(); j++) {

            if (matchedTeams[j].teamNumber < matchedTeams[minIndex].teamNumber) {

                minIndex = j;

            }

        }

        if (minIndex != i) {

            swap(matchedTeams[i], matchedTeams[minIndex]);

        }

    }

    // 输出排序后的参赛团队信息

    for (const Team& team : matchedTeams) {

        cout << "队伍编号:" << team.teamNumber << endl;

        cout << "项目名称:" << team.projectName << endl;

        cout << "参赛学校:" << team.university << endl;

        cout << "赛事类别:" << team.eventCategory << endl;

        cout << "参赛人员:" << team.participants << endl;

        cout << "指导教师:" << team.guideTeacher << endl;

        cout << "=================" << endl;

    }

}

3.4 实验结果

4 决赛叫号模拟

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

4.1 问题分析

1、所有参赛队按赛事组织文件中的赛事类别分到9个决赛室,每个决赛室容纳的参赛队数量相同。

       2、决赛室按顺序叫号,被叫号参赛队进场,比赛结束后,下一参赛队才能进赛场。

4.2 算法设计

根据赛事类别将参赛队伍分配到不同的决赛室,并按照顺序叫号让队伍进入赛场进行比赛。函数接受一个参数 `teamInfos`,即存储参赛队伍信息的向量。首先,创建一个无序映射 `categoryMap`,用于按照赛事类别将参赛队伍分组。遍历 `teamInfos` 向量中的每个参赛队伍,将其加入到对应赛事类别的向量中。然后,创建一个向量 `categories`,用于存储赛事类别,并进行排序,以确保按照字母顺序输出。接下来,定义变量 `numFinalRooms` 表示决赛室的数量,并创建一个二维向量 `finalRooms`,用于存储每个决赛室中的参赛队伍。通过遍历排序后的赛事类别向量 `categories`,将对应赛事类别的参赛队伍按顺序分配到不同的决赛室中。使用 `roomIndex` 变量追踪当前的决赛室索引,并将参赛队伍依次添加到相应的决赛室中。当 `roomIndex` 超过决赛室数量时,使用取余操作使其循环回到第一个决赛室。最后,使用循环遍历每个决赛室,并依次输出决赛室的编号。对于每个决赛室,遍历其中的参赛队伍,并输出参赛队编号,表示队伍进入赛场进行比赛。使用 `this_thread::sleep_for()` 函数模拟比赛进行,每次比赛结束前等待 0.5 秒。输出比赛结束提示,并输出空行以分隔不同的决赛室。实现了一个决赛叫号系统,将参赛队伍按照赛事类别分配到不同的决赛室,并模拟比赛叫号过程。

4.3 算法实现

// 决赛叫号系统

void EventManagementSystem::finalsCallSystem(const vector<Team>& teamInfos) {

    // 按照赛事类别将参赛队伍分组

    unordered_map<string, vector<Team>> categoryMap;

    for (const Team& info : teamInfos) {

        categoryMap[info.eventCategory].push_back(info);

    }

    // 按照赛事类别进行排序

    vector<string> categories;

    for (const auto& pair : categoryMap) {

        categories.push_back(pair.first);

    }

    sort(categories.begin(), categories.end());

    int numFinalRooms = 9; // 决赛室数量

    vector<vector<Team>> finalRooms(numFinalRooms); // 决赛室队伍

    // 将参赛队伍按照顺序分配到决赛室

    int roomIndex = 0;

    for (const string& category : categories) {

        const vector<Team>& teams = categoryMap[category];

        for (const Team& team : teams) {

            finalRooms[roomIndex].push_back(team);

            roomIndex = (roomIndex + 1) % numFinalRooms;

        }

    }

    // 模拟决赛叫号

    for (int i = 0; i < numFinalRooms; i++) {

        cout << "决赛室编号:" << i + 1 << endl;

        cout << "=================" << endl;

        const vector<Team>& teams = finalRooms[i];

        for (const Team& team : teams) {

            cout << "参赛队编号:" << team.teamNumber << " 进入赛场" << endl;

            this_thread::sleep_for(chrono::milliseconds(500));  // 比赛结束前等待0.5

        }

        cout << " 比赛结束" << endl;

        cout << endl;

    }

}

4.4 实验结果

用户通过指令选择叫号系统,就好通过算法进行模拟叫号操作。

5 校园导航

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

5.1 问题分析

使用了无序映射(`unordered_map`)来存储目的地的信息和导航图的信息。用户输入起点和终点,使用Dijkstra 算法找到最短路径并输出。

5.2 算法设计

使用了无序映射(`unordered_map`)来存储目的地的信息和导航图的信息。首先,使用无序映射 `destinationInfo` 存储了各个目的地的编号和描述信息。键为目的地的编号,值为目的地的描述。然后,使用无序映射 `navigationGraph` 存储了导航图的信息。它的键是起点的编号,值是另一个无序映射,其中键是终点的编号,值是一个二元组,包含了到达终点的距离和导航所需的时间。接下来,用户输入起点编号和终点编号,并进行判断是否有效。如果起点或终点的编号不存在于 `destinationInfo` 中,则提示输入有效的起点和终点编号。然后,使用 Dijkstra 算法计算从起点到终点的最短路径。首先初始化距离和前驱节点的映射,并将所有节点标记为未访问。然后,循环遍历未访问的节点,选择距离最小的节点作为当前节点,并更新与其相邻节点的距离和前驱节点。直到找到终点或所有节点都被访问过。如果找不到最短路径(即终点的前驱节点为-1),则输出无法找到最短路径的信息。如果找到了最短路径,将路径存储在向量 `path` 中,并输出最短路径的编号序列。最后,输出最短路径的长度,并根据编号在 `destinationInfo` 中查找并输出详细信息。实现了根据起点和终点之间的最短路径进行校园导游,并提供了路径长度和详细信息的输出。

5.3 算法实现

//导航系统

void EventManagementSystem::campusGuide() {

    unordered_map<int, string> destinationInfo = {

        {1, "行政楼,用途:办公楼"},

        {2, "海韵湖,用途:湖泊,风景优美"},

        {3, "图书馆,用途:查阅资料,自习室"},

        {4, "东食堂,用途:进餐休息"},

        {5, "东操场,用途:跑道,足球场"},

        {6, "南门,用途:学校的主门"},

        {7, "文体中心,用途:室内运动中心,船院剧场"},

        {8, "西操场,用途:跑道,足球场"},

        {9, "经世楼,用途:教学楼"},

        {10, "文理大楼,用途:实验中心,教学楼"},

        {11, "西食堂,用途:进餐休息"},

        {12, "西宿舍区,用途:学生宿舍"}

    };

    unordered_map<int, unordered_map<int, pair<int, int>>> navigationGraph = {

        {1, {{2, {300, 2}}, {10, {700, 10}}}},

        {2, {{3, {600, 3}}, {1, {300, 1}}}},

        {3, {{10, {400, 10}}, {8, {550, 8}}, {4, {100, 4}}, {2, {300, 2}}}},

        {4, {{7, {550, 7}}, {5, {100, 5}}, {3, {100, 3}}}},

        {5, {{6, {250, 6}}, {4, {100, 4}}}},

        {6, {{7, {250, 7}}, {12, {700, 12}}, {5, {250, 5}}}},

        {7, {{8, {100, 8}}, {6, {250, 6}}, {11, {300, 11}}, {4, {550, 4}}}},

        {8, {{9, {100, 9}}, {7, {100, 7}}, {11, {250, 11}}, {3, {550, 3}}}},

        {9, {{10, {100, 10}}, {8, {100, 8}}}},

        {10, {{9, {100, 9}}, {1, {700, 1}}, {3, {400, 3}}}},

        {11, {{12, {200, 12}}, {8, {250, 8}}, {7, {300, 7}}}},

        {12, {{11, {200, 11}}, {6, {700, 6}}}}

    };

    cout << "1.行政楼 " << "2.海韵湖 " << "3.图书馆 " << "4.东食堂 " << "5.东操场 \n"

        << "6.南门  " << "7.文体中心" << "8.西操场 " << "9.经世楼 " << "10.文理大楼" << "11.西食堂 " << "12.西宿舍区" << endl;

    int start, end;

    cout << "请输入起点编号:" << endl;

    cin >> start;

    cout << "请输入终点编号:" << endl;

    cin >> end;

    if (destinationInfo.find(start) == destinationInfo.end() || destinationInfo.find(end) == destinationInfo.end()) {

        cout << "请输入有效的起点和终点编号!" << endl;

        return;

    }

    unordered_map<int, int> distance;

    unordered_map<int, int> prev;

    vector<int> unvisited;

    for (const auto& pair : destinationInfo) {

        const int& destination = pair.first;

        distance[destination] = numeric_limits<int>::max();

        prev[destination] = -1;

        unvisited.push_back(destination);

    }

    distance[start] = 0;

    while (!unvisited.empty()) {

        int u = unvisited[0];

        int minDistance = distance[u];

        for (const int& destination : unvisited) {

            if (distance[destination] < minDistance) {

                u = destination;

                minDistance = distance[destination];

            }

        }

        unvisited.erase(remove(unvisited.begin(), unvisited.end(), u), unvisited.end());

        if (u == end) {

            break;

        }

        for (const auto& neighbor : navigationGraph[u]) {

            const int& v = neighbor.first;

            int altDistance = distance[u] + neighbor.second.first;

            if (altDistance < distance[v]) {

                distance[v] = altDistance;

                prev[v] = u;

            }

        }

    }

    if (prev[end] == -1) {

        cout << "无法找到最短路径" << endl;

        return;

    }

    vector<int> path;

    int current = end;

    while (current != start) {

        path.push_back(current);

        current = prev[current];

    }

    path.push_back(start);

    reverse(path.begin(), path.end());

    cout << "最短路径为:" << endl;

    for (size_t i = 0; i < path.size(); ++i) {

        cout << path[i];

        if (i != path.size() - 1) {

            cout << " -> ";

        }

    }

    cout << endl;

    cout << "路径长度为:" << distance[end] << " " << endl;

    cout << "详细信息:" << endl;

    for (const int& destination : path) {

        cout << destination << ": " << destinationInfo[destination] << endl;

    }

}

5.4 实验结果

用户根据指令选择指令,输入起点和终点,就能通过算法得到最短路径并输出最短路径,还能介绍途径地点的相关信息。

参考文献

[1]吐尔地·托合提.《数据结构》中基于二叉排序树的查找与排序算法讲解[J].现代计算机,2021(13):117-120.

[2]黄逸.选择排序算法的改进与应用[J].无线互联科技,2018,15(23):90-92.

[3]黄翼虎,孙久象.基于自动化码头的改进Dijkstra算法路径规划研究[J].电子设计工程,2023,31(08):37-41.DOI:10.14022/j.issn1674-6236.2023.08.008.

[4] 吴陈. 数据结构[M]. 第二版. 科学出版社, 2016.11.

心得体会

        数据结构课程设计是一门非常重要的课程,它旨在让学生掌握常见的数据结构和算法,并能够在实际应用中进行运用和实现。在这个过程中,学生需要将所学的理论知识与实际应用相结合,从而解决一些实际问题。

在课程设计中,我选择了基于二叉树的算法设计,我学习了二叉树的遍历方法和平衡二叉树的实现。我实现了二叉树的构建、遍历和查找等操作,并使用递归和非递归算法解决了二叉树的一系列问题,例如二叉树中度和二叉树高度的计算、查找最小生成树等。通过这次课程设计,我深入理解了数据结构在计算机科学中的重要性。数据结构是一种基础性的技术,它可以帮助我们更好地组织和存储数据,从而使我们可以更加高效地处理数据、搜索数据和提取数据。在课程设计中,我也体会到了算法设计的重要性,算法可以帮助我们更加高效地解决问题,提高程序的效率。

        本次课程设计的选题非常有意义,与实际应用紧密相关。通过本次课程设计,我学会了如何使用二叉排序树进行查找,以及如何实现基本的赛事队伍信息管理功能,例如增加、删除、修改参赛队伍的信息等。      

        在实现二叉排序树时,需要对数据进行比较和排序。我使用了C++语言中的比较运算符和sort函数来实现。在实现参赛队伍信息管理模块时,我使用了C++语言中的文件输入输出流,将数据存储在文件中,并进行读取和写入操作。

        其次,在实现决赛叫号系统时,我遇到了不少问题,最终通过查阅资料解决了。在实现按参赛学校查询参赛团队的功能时,我使用了C++语言中的字符串比较函数和二叉排序树来查找参赛队伍。为了实现按赛事类别查询参赛团队的功能,我使用了C++语言中的switch语句和二叉排序树来查找参赛队伍。在实现校园导游程序时,我选择了使用Dijkstra算法来计算两个目标地之间的最短路径。在实现Dijkstra算法时,我使用了C++语言中的优先队列和邻接表来存储数据和计算最短路径。在提供不少于10个目标地的导航时,我对校园地图进行了全面梳理,将校园地图中所有重要的建筑物作为目标地,并提供导航查询。

        通过这次课程设计,我进一步掌握了数据结构和算法的基本知识,并提高了我的编程能力。数据结构课程设计是一门非常有意义的课程,通过这次课程设计,我深入理解了数据结构的基本原理和应用,并掌握了一些实际的编程技巧。我相信这些知识和技能将对我未来的学习和工作产生积极的影响。

附源码:

//EventManagementSystem.cpp文件

#include "EventManagementSystem.h"
#include<iostream>
#include<fstream>
#include<sstream>
#include<algorithm>
#include <mutex>
#include <chrono>
#include<vector>
#include <unordered_map>
using namespace std;


TeamNode* root;//二叉排序树根节点

//插入节点
void EventManagementSystem::insertNode(TeamNode* current, TeamNode* newNode)//需要当前节点和新节点作为参数
{
    //如果新节点比当前节点小
    if (newNode->team.teamNumber.compare(current->team.teamNumber) < 0) {
        //如果当前节点的左子树为空则放在左子树
        if (current->left == nullptr) {
            current->left = newNode;
        }
        //如果当前节点的左子树不空则继续寻找当前节点左子树的空的左子树
        else {
            insertNode(current->left, newNode);
        }
    }
    //如果新节点比当前节点大
    else {
        //如果当前节点的右子树为空则放在右子树
        if (current->right == nullptr) {
            current->right = newNode;
        }
        else
        {
            //如果当前节点的右子树不空则继续寻找当前节点右子树的空的右子树
            insertNode(current->right, newNode);
        }
    }
}

//将赛事信息从team.txt文件中读取出来并放入二叉排序树中
void EventManagementSystem::loadTeamsFromFile()
{
    ifstream inFile("team.txt");//打开文件
    if (!inFile) {
        cout << "无法打开文件 team.txt\n";
        return;
    }
    //从输入流中读取一行文本,并将其存储到一个字符串变量中
    string line;
    getline(inFile, line); // 读取文件中的标题行,忽略

    while (getline(inFile, line)) {
        // 使用 stringstream 进行分割
        stringstream ss(line);
        string temp;

        string teamNumber;
        getline(ss, teamNumber, '\t');
        ss.ignore();

        getline(ss, temp, '\t'); // 跳过参赛作品名称的 #
        string projectName;
        getline(ss, projectName, '\t');
        ss.ignore();

        getline(ss, temp, '\t'); // 跳过参赛学校的 #
        string university;
        getline(ss, university, '\t');
        ss.ignore();

        getline(ss, temp, '\t'); // 跳过赛事类别的 #
        string eventCategory;
        getline(ss, eventCategory, '\t');
        ss.ignore();

        getline(ss, temp, '\t'); // 跳过参赛者的 #
        string participants;
        getline(ss, participants, '\t');
        ss.ignore();

        getline(ss, temp, '\t'); // 跳过指导教师的 #
        string guideTeacher;
        getline(ss, guideTeacher);

       
        // 输出格式化的信息
        cout << "参赛队伍编号: " << teamNumber << endl;
        cout << "参赛作品名称: " << projectName << endl;
        cout << "参赛学校: " << university << endl;
        cout << "赛事类别: " << eventCategory << endl;
        cout << "参赛者: " << participants << endl;
        cout << "指导教师: " << guideTeacher << endl;
       
       // 为当前行的信息用队伍编号创建一个新的队伍节点
        TeamNode* newNode = new TeamNode(teamNumber,projectName,university,eventCategory,participants,guideTeacher);
        //如果根节点为空则当前节点就是根节点
        if (root == nullptr)
        {
            root = newNode;
        }
        //如果根节点不空,则将创建的新节点插入到二叉排序树中
        else
        {
            insertNode(root, newNode);
        }
        cout << endl;
    }
    //读取完毕,关闭文件
    inFile.close();
    cout << "数据已从 team.txt 文件读取并输出。\n";
}


//添加队伍信息
void EventManagementSystem::addTeam()
{
    //首先创建新节点,以便存放需要添加的队伍信息
    TeamNode* newNode = new TeamNode;

    // 从用户输入获取参赛队伍信息
    cout << "请输入参赛队伍编号:";
    cin.ignore(); // 忽略之前的换行符
    getline(cin, newNode->team.teamNumber);
    cout << "请输入参赛作品名称:";
    getline(cin, newNode->team.projectName);
    cout << "请输入参赛学校:";
    getline(cin, newNode->team.university);
    cout << "请输入赛事类别:";
    getline(cin, newNode->team.eventCategory);
    cout << "请输入参赛者:";
    getline(cin, newNode->team.participants);
    cout << "请输入指导教师:";
    getline(cin, newNode->team.guideTeacher);
    //在txt文件中修改
    ofstream mycout("team.txt", ios::app);
    mycout << newNode->team.teamNumber << "	#	" << newNode->team.projectName << "	#	"  << newNode->team.university << "	#	" << newNode->team.eventCategory << "	#	" << newNode->team.participants << "	#	" << newNode->team.guideTeacher << endl;
    mycout.close();

    
    //相关信息录入完成,将当前节点的左右子树分别赋为空
    newNode->left = nullptr;
    newNode->right = nullptr;

    // 插入节点到二叉排序树
    //如果根节点为空,则当前节点就是根节点
    if (root == nullptr) {
        root = newNode;
    }
    //否则将当前节点插入二叉排序树中
    else {
        insertNode(root, newNode);
    }
    //给出添加成功的提示
    cout << "参赛队伍信息已添加。\n";
}
//查找按队伍编号节点
TeamNode* EventManagementSystem::searchNode(TeamNode* current, const string& teamNumber)//需要当前节点和要查找的队伍编号信息
{
    //如果当前节点为空或者当前节点的队伍编号等于需要查找的队伍编号,则返回当前节点
    if (current == nullptr || current->team.teamNumber.compare(teamNumber) == 0) {
        return current;
    }
    //需要查找的节点编号比当前节点编号小,则去当前节点的左子树中继续寻找
    if (teamNumber.compare(current->team.teamNumber) < 0) {
        return searchNode(current->left, teamNumber);
    }
    //需要查找的节点编号比当前节点编号大,则去当前节点的右子树中继续寻找
    else {
        return searchNode(current->right, teamNumber);
    }
}
//计算二叉树中指定节点的高度
int EventManagementSystem::getHeight(TeamNode* node)
{
    if (node == nullptr) {
        return 0;
    }

    int leftHeight = getHeight(node->left);
    int rightHeight = getHeight(node->right);

    return 1 + max(leftHeight, rightHeight);
}
//计算二叉树中指定节点的平衡因子
int EventManagementSystem::getBalanceFactor(TeamNode* node)
{
    if (node == nullptr) {
        return 0;
    }
    int leftHeight = getHeight(node->left);
    int rightHeight = getHeight(node->right);
    return leftHeight - rightHeight;
}
//左旋
TeamNode* EventManagementSystem::rotateLeft(TeamNode* node)
{
    //当前节点的右子节点保存在newRoot中
    TeamNode* newRoot = node->right;
    //原右子节点的左子节点保存在subtree中
    TeamNode* subtree = newRoot->left;

    // 左旋
    //将当前节点变为变为newRoot的左子节点
    newRoot->left = node;
    //将原右子节点的左子节点变为当前节点的右子节点
    node->right = subtree;
    //返回新的根节点(当前节点的右子节点)
    return newRoot;
}
//右旋(同上)
TeamNode* EventManagementSystem::rotateRight(TeamNode* node)
{
    TeamNode* newRoot = node->left;
    TeamNode* subtree = newRoot->right;

    // 右旋
    newRoot->right = node;
    node->left = subtree;

    return newRoot;
}
//找最小节点
TeamNode* EventManagementSystem::findMinimumNode(TeamNode* current)
{
    //当左子树不空时找到最左边的节点就是最小节点
    while (current->left != nullptr) {
        current = current->left;
    }
    //左子树为空则当前节点就是最小节点
    return current;
}
//在文件中删除
void EventManagementSystem::deletefile(string id)
{
    ifstream inputFile("team.txt");

    if (!inputFile) {
        cout << "无法打开文件 team.txt\n";
        return;
    }

    // 创建一个临时文件
    ofstream outputFile("temp.txt");

    if (!outputFile) {
        cout << "无法创建文件 temp.txt\n";
        inputFile.close();
        return;
    }

    std::string line;
    std::string contentToRemove = id;  // 要删除的内容

    while (std::getline(inputFile, line))
    {
        if (line.find(contentToRemove) != std::string::npos)
        {
            // 跳过包含要删除内容的行
            continue;
        }

        // 将不包含要删除内容的行写入临时文件中
        outputFile << line << std::endl;
    }

    inputFile.close();
    outputFile.close();

    std::remove("team.txt");        // 删除原始文件
    std::rename("temp.txt", "team.txt");  // 将临时文件重命名为原始文件的名称
}
//在二叉树中找到需要删除的节点
TeamNode* EventManagementSystem::deleteNode(TeamNode* current, const string& teamNumber)
{
    // 如果当前节点为空,则返回当前节点
    if (current == nullptr) {
        return current;
    }
    // 如果需要删除的节点编号比当前节点编号小,则在前节点的左孩子中找需要删除的节点并赋给当前节点的左孩子
    if (teamNumber.compare(current->team.teamNumber) < 0) {
        current->left = deleteNode(current->left, teamNumber);
    }
    // 如果需要删除的节点编号比当前节点编号大,则在前节点的右孩子中找需要删除的节点并赋给当前节点的右孩子
    else if (teamNumber.compare(current->team.teamNumber) > 0) {
        current->right = deleteNode(current->right, teamNumber);
    }
    //需要删除的节点编号和当前节点编号相等
    else {
        // 找到要删除的节点

        // 如果节点为叶子节点或只有一个子节点
        if (current->left == nullptr) {
            TeamNode* temp = current->right;
            delete current;
            return temp;
        }
        else if (current->right == nullptr) {
            TeamNode* temp = current->left;
            delete current;
            return temp;
        }

        // 如果节点有两个子节点
        TeamNode* successor = findMinimumNode(current->right);
        current->team = successor->team;
        current->right = deleteNode(current->right, successor->team.teamNumber);
    }

    // 进行平衡调整
    int balanceFactor = getBalanceFactor(current);

    // 右旋操作
    if (balanceFactor > 1 && getBalanceFactor(current->left) >= 0) {
        return rotateRight(current);
    }

    // 左旋操作
    if (balanceFactor < -1 && getBalanceFactor(current->right) <= 0) {
        return rotateLeft(current);
    }

    // 左右旋操作
    if (balanceFactor > 1 && getBalanceFactor(current->left) < 0) {
        current->left = rotateLeft(current->left);
        return rotateRight(current);
    }

    // 右左旋操作
    if (balanceFactor < -1 && getBalanceFactor(current->right) > 0) {
        current->right = rotateRight(current->right);
        return rotateLeft(current);
    }
    //返回当前节点作为删除后的根节点
    return current;
}
//删除参赛队伍信息
void EventManagementSystem::deleteTeam(string teamNumber)
{
    //如果根节点为空,说明本来就没有参赛队伍信息,无法进行删除
    if (root == nullptr) {
        cout << "当前无参赛队伍信息。\n";
        return;
    }

    // 查找要删除的节点,将查找到的节点赋值给一个新的节点targetNode
    TeamNode* targetNode = searchNode(root, teamNumber);
    //如果找不到要删除的节点,给出提示
    if (targetNode == nullptr) {
        cout << "未找到对应的参赛队伍信息。\n";
        return;
    }
    //找到要删除的节点
    // 执行删除操作
    root = deleteNode(root, teamNumber);
    deletefile(teamNumber);
    cout << "参赛队伍信息已删除。\n";
}


//修改队伍信息
// 在文本文件中修改参赛队伍信息
void EventManagementSystem::updateInFile(string teamNumber, const Team& updatedInfo) {
    ifstream inputFile("team.txt");
    if (!inputFile.is_open()) {
        cout << "无法打开文档" << endl;
        return;
    }

    ofstream outputFile("temp.txt");
    if (!outputFile.is_open()) {
        cout << "无法创建临时文件" << endl;
        inputFile.close();
        return;
    }
    TeamNode* node = searchNode(root, teamNumber);
    string line;
    while (getline(inputFile, line)) {
        stringstream ss(line);
        string currentTeamNumber;
        ss >> currentTeamNumber;
        if (currentTeamNumber.compare(teamNumber) == 0) {
            // 找到要修改的队伍信息,将新的信息写入临时文件
            outputFile << teamNumber << "	#	" << updatedInfo.projectName << "	#	" << updatedInfo.university << "	#	" << updatedInfo.eventCategory << "	#	" << updatedInfo.participants << "	#	" << updatedInfo.guideTeacher << endl;    
        }
        else {
            // 将原始行写入临时文件
            outputFile << line << endl;
        }
    }

    inputFile.close();
    outputFile.close();

    if (searchNode(root, teamNumber) != NULL) {
        remove("team.txt");       // 删除原始的队伍信息文件
        rename("temp.txt", "team.txt");  // 将临时文件重命名为原始的队伍信息文件

    }
    else {
        remove("temp.txt");  // 删除临时文件
        cout << "未找到指定的队伍信息,无法修改。" << endl;
    }
}

//修改
void EventManagementSystem::modifyTeam()
{
    if (root == nullptr) {
        cout << "当前无参赛队伍信息。\n";
        return;
    }

    string teamNumber;
    cout << "请输入要修改的参赛队伍编号:";
    cin >> teamNumber;

    TeamNode* target = searchNode(root, teamNumber);
    if (target == nullptr) {
        cout << "未找到对应的参赛队伍信息。\n";
        return;
    }

    // 根据需要,修改参赛队伍的字段信息
    cout << "请输入新的参赛作品名称:";
    cin.ignore();
    getline(cin, target->team.projectName);
    cout << "请输入新的参赛学校:";
    getline(cin, target->team.university);
    cout << "请输入新的赛事类别:";
    getline(cin, target->team.eventCategory);
    cout << "请输入新的参赛者:";
    getline(cin, target->team.participants);
    cout << "请输入新的指导教师:";
    getline(cin, target->team.guideTeacher);

    updateInFile(teamNumber, target->team);
    // 输出修改成功的提示信息
    cout << "参赛队伍信息已修改并保存到 team.txt 文件中。\n";
}

void EventManagementSystem::Management()
{
    int choice;
    loadTeamsFromFile(); // 从文件读取数据并存入二叉树

    do {
        cout << "============= 菜单 =============\n";
        cout << "1. 增加参赛队伍信息\n";
        cout << "2. 删除参赛队伍信息\n";
        cout << "3. 修改参赛队伍信息\n";
        cout << "0. 退出\n";
        cout << "请输入选项:";
       cin >> choice;

        switch (choice) {
        case 1:
            addTeam();
            break;
        case 2:
        {
            string teamNumber;
            cout << "请输入需要删除的队伍编号:";
            cin >> teamNumber;
            deleteTeam(teamNumber);
            break;
        }
        case 3:
            modifyTeam();
            break;
        case 0:
            cout << "程序已退出。\n";
            break;
        default:
            cout << "无效选项,请重新输入。\n";
            break;
        }
    } while (choice != 0);
}
//计算路径长度
int EventManagementSystem::calculatePathLength(TeamNode* node, const string& teamNumber, int level) {
    if (node == nullptr) {
        return 0;  // 未找到目标节点
    }
    if (teamNumber < node->team.teamNumber) {
        return 1 + calculatePathLength(node->left, teamNumber, level + 1);
    }
    else if (teamNumber > node->team.teamNumber) {
        return 1 + calculatePathLength(node->right, teamNumber, level + 1);
    }
    else {
        return level;  // 找到目标节点,返回路径长度
    }
}
//按编号查找队伍并输出ASL
void EventManagementSystem::searchTeam()
{
    int totalPathLength = 0;
    int nodeCount = 0;
    //如果根节点为空,说明本来就没有参赛队伍信息,无法进行查询
    if (root == nullptr) {
        cout << "当前无参赛队伍信息。\n";
        return;
    }

    string teamNumber;
    cout << "请输入要查询的参赛队伍编号:";
    cin >> teamNumber;

    // 查找要查询的节点,将查找到的节点赋值给一个新的节点targetNode
    TeamNode* targetNode = searchNode(root, teamNumber);
    //如果找不到要查询的节点,给出提示
    if (targetNode == nullptr) {
        cout << "未找到对应的参赛队伍信息。\n";
        return;
    }

    // 查找要查询的节点并计算路径长度
    int pathLength = calculatePathLength(root, teamNumber, 1);
    if (pathLength == 0) {
        cout << "未找到对应的参赛队伍信息。\n";
        return;
    }

    totalPathLength += pathLength;
    nodeCount++;


    //找到要查询的节点
    cout << "参赛队伍编号: " << targetNode->team.teamNumber << endl;
    cout << "参赛作品名称: " << targetNode->team.projectName << endl;
    cout <<"参赛学校: " << targetNode->team.university << endl;
    cout << "赛事类别: "<<targetNode->team.eventCategory << endl;
    cout << "参赛者: "<<targetNode->team.participants << endl;
    cout << "指导教师: " <<targetNode->team.guideTeacher << endl;
    
    double asl = static_cast<double>(totalPathLength) / nodeCount;
    cout << "平均查找长度ASL:" << asl << endl;
    cout << "ASL的计算公式:" << endl;
    cout << "ASL = (h1 + h2 + ... + hn) / n" << endl;
    cout << "其中,hi 表示查找到的参赛队所在的层级,n 表示节点的总数" << endl;
}
//按学校查找队伍
void EventManagementSystem::searchTeamByUniversity(const string& university, const vector<Team>& teamInfos) {
    vector<Team> matchedTeams;

    // 遍历参赛队伍,找到匹配的队伍信息
    for (const Team& team : teamInfos) {
        if (team.university == university) {
            matchedTeams.push_back(team);
        }
    }

    if (matchedTeams.empty()) {
        cout << "未找到匹配的队伍信息" << endl;
        return;
    }

    // 使用选择排序按参赛队编号排序(选择未排序队伍中的最小元素与当前元素交换位置)
    for (int i = 0; i < matchedTeams.size() - 1; i++) {
        int minIndex = i;
        for (int j = i + 1; j < matchedTeams.size(); j++) {
            if (matchedTeams[j].teamNumber < matchedTeams[minIndex].teamNumber) {
                minIndex = j;
            }
        }
        if (minIndex != i) {
            swap(matchedTeams[i], matchedTeams[minIndex]);
        }
    }

    // 输出排序后的参赛团队信息
    for (const Team& team : matchedTeams) {
        cout << "队伍编号:" << team.teamNumber << endl;
        cout << "项目名称:" << team.projectName << endl;
        cout << "参赛学校:" << team.university << endl;
        cout << "赛事类别:" << team.eventCategory << endl;
        cout << "参赛人员:" << team.participants << endl;
        cout << "指导教师:" << team.guideTeacher << endl;
        cout << "=================" << endl;
    }
}


// 决赛叫号系统
void EventManagementSystem::finalsCallSystem(const vector<Team>& teamInfos) {
    // 按照赛事类别将参赛队伍分组
    unordered_map<string, vector<Team>> categoryMap;
    for (const Team& info : teamInfos) {
        categoryMap[info.eventCategory].push_back(info);
    }

    // 按照赛事类别进行排序
    vector<string> categories;//存储赛事类别
    for (const auto& pair : categoryMap) {
        categories.push_back(pair.first);
    }
    sort(categories.begin(), categories.end());

    int numFinalRooms = 9; // 决赛室数量
    vector<vector<Team>> finalRooms(numFinalRooms); // 决赛室队伍

    // 将参赛队伍按照顺序分配到决赛室
    int roomIndex = 0;
    for (const string& category : categories) {
        const vector<Team>& teams = categoryMap[category];
        for (const Team& team : teams) {
            finalRooms[roomIndex].push_back(team);
            roomIndex = (roomIndex + 1) % numFinalRooms;
        }
    }

    // 模拟决赛叫号
    for (int i = 0; i < numFinalRooms; i++) {
        cout << "决赛室编号:" << i + 1 << endl;
        cout << "=================" << endl;

        const vector<Team>& teams = finalRooms[i];
        for (const Team& team : teams) {
            cout << "参赛队编号:" << team.teamNumber << " 进入赛场" << endl;
            this_thread::sleep_for(chrono::milliseconds(500));  // 比赛结束前等待0.5秒
        }
        cout << " 比赛结束" << endl;
        cout << endl;
    }
}


//导航系统
void EventManagementSystem::campusGuide() {
    unordered_map<int, string> destinationInfo = {
        {1, "行政楼,用途:办公楼"},
        {2, "海韵湖,用途:湖泊,风景优美"},
        {3, "图书馆,用途:查阅资料,自习室"},
        {4, "东食堂,用途:进餐休息"},
        {5, "东操场,用途:跑道,足球场"},
        {6, "南门,用途:学校的主门"},
        {7, "文体中心,用途:室内运动中心,船院剧场"},
        {8, "西操场,用途:跑道,足球场"},
        {9, "经世楼,用途:教学楼"},
        {10, "文理大楼,用途:实验中心,教学楼"},
        {11, "西食堂,用途:进餐休息"},
        {12, "西宿舍区,用途:学生宿舍"}
    };

    unordered_map<int, unordered_map<int, pair<int, int>>> navigationGraph = {
        {1, {{2, {300, 2}}, {10, {700, 10}}}},
        {2, {{3, {600, 3}}, {1, {300, 1}}}},
        {3, {{10, {400, 10}}, {8, {550, 8}}, {4, {100, 4}}, {2, {300, 2}}}},
        {4, {{7, {550, 7}}, {5, {100, 5}}, {3, {100, 3}}}},
        {5, {{6, {250, 6}}, {4, {100, 4}}}},
        {6, {{7, {250, 7}}, {12, {700, 12}}, {5, {250, 5}}}},
        {7, {{8, {100, 8}}, {6, {250, 6}}, {11, {300, 11}}, {4, {550, 4}}}},
        {8, {{9, {100, 9}}, {7, {100, 7}}, {11, {250, 11}}, {3, {550, 3}}}},
        {9, {{10, {100, 10}}, {8, {100, 8}}}},
        {10, {{9, {100, 9}}, {1, {700, 1}}, {3, {400, 3}}}},
        {11, {{12, {200, 12}}, {8, {250, 8}}, {7, {300, 7}}}},
        {12, {{11, {200, 11}}, {6, {700, 6}}}}
    };

    cout << "1.行政楼 " << "2.海韵湖 " << "3.图书馆 " << "4.东食堂 " << "5.东操场 \n"
        << "6.南门  " << "7.文体中心" << "8.西操场 " << "9.经世楼 " << "10.文理大楼" << "11.西食堂 " << "12.西宿舍区" << endl;

    int start, end;
    cout << "请输入起点编号:" << endl;
    cin >> start;
    cout << "请输入终点编号:" << endl;
    cin >> end;

    if (destinationInfo.find(start) == destinationInfo.end() || destinationInfo.find(end) == destinationInfo.end()) {
        cout << "请输入有效的起点和终点编号!" << endl;
        return;
    }

    unordered_map<int, int> distance;//距离
    unordered_map<int, int> prev;//前驱节点
    vector<int> unvisited;//未访问容器
    //初始化距离和前驱节点,将所有节点标记为未访问
    for (const auto& pair : destinationInfo) {
        const int& destination = pair.first;
        distance[destination] = numeric_limits<int>::max();
        prev[destination] = -1;
        unvisited.push_back(destination);
    }
    distance[start] = 0;
    //循环遍历未访问的节点
    while (!unvisited.empty()) {
        int u = unvisited[0];
        int minDistance = distance[u];
        for (const int& destination : unvisited) {
            if (distance[destination] < minDistance) {
                u = destination;
                minDistance = distance[destination];//选择距离最小的节点作为当前节点
            }
        }

        unvisited.erase(remove(unvisited.begin(), unvisited.end(), u), unvisited.end());

        if (u == end) {
            break;
        }
        //更新与其相邻节点的距离和前驱节点
        for (const auto& neighbor : navigationGraph[u]) {
            const int& v = neighbor.first;
            int altDistance = distance[u] + neighbor.second.first;
            if (altDistance < distance[v]) {
                distance[v] = altDistance;
                prev[v] = u;
            }
        }
    }

    if (prev[end] == -1) {
        cout << "无法找到最短路径" << endl;
        return;
    }

    vector<int> path;
    int current = end;
    while (current != start) {
        path.push_back(current);
        current = prev[current];
    }
    path.push_back(start);

    reverse(path.begin(), path.end());

    cout << "最短路径为:" << endl;
    for (size_t i = 0; i < path.size(); ++i) {
        cout << path[i];
        if (i != path.size() - 1) {
            cout << " -> ";
        }
    }
    cout << endl;

    cout << "路径长度为:" << distance[end] << " 米" << endl;
    cout << "详细信息:" << endl;
    for (const int& destination : path) {
        cout << destination << ": " << destinationInfo[destination] << endl;
    }
}

//EventManagementSystem.h文件

#pragma once
#include<string>
#include<vector>
using namespace std;

// 参赛队伍结构体
struct Team {
	string teamNumber;
	string projectName;
	string university;
	string eventCategory;
	string participants;
	string guideTeacher;
};

// 参赛队伍节点结构体
struct TeamNode {
	Team team;
	TeamNode* left;
	TeamNode* right;
	int height = 1;
	TeamNode()
	{
		this->team.teamNumber = team.teamNumber;
		this->team.projectName = team.projectName;
		this->team.eventCategory = team.eventCategory;
		this->team.participants = team.participants;
		this->team.university = team.university;
		this->team.guideTeacher = team.guideTeacher;
		
		left = nullptr;
		right = nullptr;
	}
	
	// 构造函数,接受队伍编号作为参数
	TeamNode(const string& number, const string& projectName,const string& university, const string& eventCategory, const string& participants, const string& guideTeacher) {
		TeamNode* node = new TeamNode();
		team.teamNumber = number;
		team.projectName = projectName;
		team.university = university;
		team.eventCategory = eventCategory;
		team.participants = participants;
		team.guideTeacher = guideTeacher;
		left = nullptr;
		right = nullptr;
	}
};
class EventManagementSystem
{
public:
	void deleteTeam(string teamNumber);
	void deletefile(string id);
	TeamNode* rotateLeft(TeamNode* node);
	TeamNode* rotateRight(TeamNode* node);
	int getHeight(TeamNode* node);
	int getBalanceFactor(TeamNode* node);
	TeamNode* searchNode(TeamNode* current, const string& teamNumber);
	TeamNode* deleteNode(TeamNode* current, const string& teamNumber);
	TeamNode* findMinimumNode(TeamNode* current);
	void updateInFile(string teamNumber, const Team& updatedInfo);
	void modifyTeam();
	void Management();
	void searchTeam();
	void searchTeamByUniversity(const string& university, const vector<Team>& teamInfos);
	void finalsCallSystem(const vector<Team>& teamInfos);
	void insertNode(TeamNode* current, TeamNode* newNode);
	void loadTeamsFromFile();
	void addTeam();
	void campusGuide();
	int calculatePathLength(TeamNode* node, const string& teamNumber, int level);
};

//源.cpp文件

#include<iostream>
#include<vector>
#include<fstream>
#include<sstream>
#include"EventManagementSystem.h"

using namespace std;

int main()
{
    int choice;
    
    do {
        cout << "============= 菜单 =============\n";
        cout << "1. 管理参赛队伍信息\n";
        cout << "2. 读取参赛队伍信息\n";
        cout << "3. 查询参赛队伍信息\n";
        cout << "4. 叫号系统\n";
        cout << "5. 导航服务\n";
        cout << "0. 退出\n";
        cout << "请输入选项:";
        cin >> choice;

        switch (choice) {
        case 1:
            EventManagementSystem a;
            a.Management();
            break;
        case 2:
            EventManagementSystem b;
            b.loadTeamsFromFile();
            cout << "是否输入队伍编号查询参赛队伍(1.是,2.否)";
            int choice2;
            cin >> choice2;
            if (choice2 == 1)
            {
                b.searchTeam();
            }
            else {
                break;
            }
            break;
        case 3: {
            EventManagementSystem c;

            
            vector<Team> team1;
            ifstream inFile("team.txt");//打开文件
            if (!inFile) {
                cout << "无法打开文件 team.txt\n";
                return 0;
            }
            //从输入流中读取一行文本,并将其存储到一个字符串变量中
            string line;
            getline(inFile, line); // 读取文件中的标题行,忽略

            while (getline(inFile, line)) {
                // 使用 stringstream 进行分割
                stringstream ss(line);
                string temp;

                string teamNumber;
                getline(ss, teamNumber, '\t');
                ss.ignore();

                getline(ss, temp, '\t'); // 跳过参赛作品名称的 #
                string projectName;
                getline(ss, projectName, '\t');
                ss.ignore();

                getline(ss, temp, '\t'); // 跳过参赛学校的 #
                string university;
                getline(ss, university, '\t');
                ss.ignore();

                getline(ss, temp, '\t'); // 跳过赛事类别的 #
                string eventCategory;
                getline(ss, eventCategory, '\t');
                ss.ignore();

                getline(ss, temp, '\t'); // 跳过参赛者的 #
                string participants;
                getline(ss, participants, '\t');
                ss.ignore();

                getline(ss, temp, '\t'); // 跳过指导教师的 #
                string guideTeacher;
                getline(ss, guideTeacher);

                Team team;
                team.teamNumber = teamNumber;
                team.projectName = projectName;
                team.university = university;
                team.eventCategory = eventCategory;
                team.participants = participants;
                team.guideTeacher = guideTeacher;

                team1.push_back(team);
            }
            cout << "请输入需要查找的学校名称:";
            string university;
            cin >> university;
            c.searchTeamByUniversity(university,team1);
            break;
        }
        case 4: {
            EventManagementSystem d;
            vector<Team> team1;
            ifstream inFile("team.txt");//打开文件
            if (!inFile) {
                cout << "无法打开文件 team.txt\n";
                return 0;
            }
            //从输入流中读取一行文本,并将其存储到一个字符串变量中
            string line;
            getline(inFile, line); // 读取文件中的标题行,忽略
            
            while (getline(inFile, line)) {
                // 使用 stringstream 进行分割
                stringstream ss(line);
                string temp;

                string teamNumber;
                getline(ss, teamNumber, '\t');
                ss.ignore();

                getline(ss, temp, '\t'); // 跳过参赛作品名称的 #
                string projectName;
                getline(ss, projectName, '\t');
                ss.ignore();

                getline(ss, temp, '\t'); // 跳过参赛学校的 #
                string university;
                getline(ss, university, '\t');
                ss.ignore();

                getline(ss, temp, '\t'); // 跳过赛事类别的 #
                string eventCategory;
                getline(ss, eventCategory, '\t');
                ss.ignore();

                getline(ss, temp, '\t'); // 跳过参赛者的 #
                string participants;
                getline(ss, participants, '\t');
                ss.ignore();

                getline(ss, temp, '\t'); // 跳过指导教师的 #
                string guideTeacher;
                getline(ss, guideTeacher);

                Team team;
                team.teamNumber = teamNumber;
                team.projectName = projectName;
                team.university = university;
                team.eventCategory = eventCategory;
                team.participants = participants;
                team.guideTeacher = guideTeacher;

                team1.push_back(team);
            }
            d.finalsCallSystem(team1);
                break;
        }
        case 5:
        {
            EventManagementSystem e;
            e.campusGuide();
            break;
        }
            
        case 0:
            cout << "程序已退出。\n";
            break;
        default:
            cout << "无效选项,请重新输入。\n";
            break;
        }
    } while (choice != 0);
    return 0;
}

  • 7
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值