一、任务分析
本次课程设计要求协助中国大学生计算机设计大赛江苏省组委会,设计一款赛事管理系统,实现赛务相关的数据管理及信息服务。该系统能够为省级赛事管理解决以下问题:
- 能够管理各参赛队的基本信息(包含参赛队编号,参赛作品名称,参赛学校,赛事类别,参赛者,指导老师),赛事类别共11项;包括增加、删除、修改参赛队伍的信息。
- 从team.txt中读取参赛队伍的基本信息,实现基于二叉排序树的查找。根据提示输入参赛队编号,若查找成功,输出该赛事类别对应的基本信息,同时,输出查找成功时的平均查找长度ASL;否则,输出“查找失败!”。
- 能够提供按参赛学校查询参赛团队(或根据赛事类别查询参赛团队),即,根据提示输入参赛学校名称(赛事类别),若查找成功,输出该学校参赛的(该赛事类别的)所有团队的基本信息,输出的参赛团队按赛事类别有序输出。
- 为省赛现场设计一个决赛叫号系统。所有参赛队按赛事组织文件中的赛事类别分到9个决赛室,决赛室按顺序叫号,被叫号参赛队进场,比赛结束后,下一参赛队才能进赛场。请模拟决赛叫号系统,演示省赛现场各决赛室的参赛队进场情况。
- 赛事系统为参赛者提供赛地的校园导游程序,为参赛者提供各种路径导航的查询服务。以我校长山校区提供比赛场地为例,(请为参赛者提供不少于10个目标地的导航)。可为参赛者提供校园地图中任意目标地(建筑物)相关信息的查询;提供任意两个目标地(建筑物)的导航查询,即查询任意两个目的地(建筑物)之间的一条最短路径。
二、模块划分
根据任务分析,我们可以将系统划分为以下模块:
- 参赛队伍信息管理模块:包括增加、删除、修改参赛队伍的信息,以及从team.txt中读取参赛队伍信息的功能。
- 参赛队伍信息查询模块:包括基于二叉排序树的参赛队伍信息查找,以及按参赛学校查询参赛团队(或根据赛事类别查询参赛团队)的功能。
- 决赛叫号系统模块:模拟决赛叫号系统,演示省赛现场各决赛室的参赛队进场情况。
- 赛地导航查询模块:提供校园地图中任意目标地(建筑物)相关信息的查询,以及提供任意两个目标地(建筑物)的导航查询的功能。
三、数据结构设计
- 参赛队伍信息的数据结构设计:
typedef struct {
char team_id[10]; // 参赛队编号
char team_name[50]; // 参赛作品名称
char team_school[50]; // 参赛学校
int team_category; // 赛事类别,共11项
char team_members[100]; // 参赛者
char team_teacher[50]; // 指导老师
} Team;
- 根据参赛队编号查找参赛队伍信息的二叉排序树的数据结构设计:
typedef struct node {
Team team; // 参赛队伍信息
struct node* lchild; // 左子树
struct node* rchild; // 右子树
} Node;
四、算法设计
- 参赛队伍信息的增加、删除、修改操作的算法设计:
void add_team(Team team); // 增加参赛队伍信息
void delete_team(char* team_id); // 删除参赛队伍信息
void modify_team(char* team_id, Team new_team); // 修改参赛队伍信息
- 从team.txt中读取参赛队伍信息的算法设计:
void read_teams(); // 从team.txt中读取参赛队伍信息
- 根据参赛队编号查找参赛队伍信息的二叉排序树的算法设计:
Node* find_team(Node* root, char* team_id); // 查找参赛队伍信息
double get_asl(Node* root, char* team_id, int level); // 计算平均查找长度ASL
- 按参赛学校查询参赛团队(或根据赛事类别查询参赛团队)的算法设计:
void search_by_school(char* school); // 按参赛学校查询参赛团队
void search_by_category(int category); // 根据赛事类别查询参赛团队
- 决赛叫号系统的算法设计:
void call_teams(); // 模拟决赛叫号系统
- 赛地导航查询的算法设计:
void show_map(); // 展示校园地图
void search_location(); // 根据建筑物名称查询相关信息
void search_path(); // 查询两个建筑物之间的最短路径
五、界面设计
根据模块划分,我们可以设计出以下菜单:
- 参赛队伍信息管理菜单:
1.1 增加参赛队伍信息
1.2 删除参赛队伍信息
1.3 修改参赛队伍信息
- 参赛队伍信息查询菜单:
2.1 根据参赛队编号查找参赛队伍信息
2.2 按参赛学校查询参赛团队
2.3 根据赛事类别查询参赛团队
- 决赛叫号系统菜单:
3.1 模拟决赛叫号系统
- 赛地导航查询菜单:
4.1 展示校园地图
4.2 根据建筑物名称查询相关信息
4.3 查询两个建筑物之间的最短路径
程旭如下
#include<thread>
#include<chrono>
#include<queue>
#include<iostream>
#include <fstream>
#include <string>
#include<vector>
#include<iomanip>
#include<sstream>
using namespace std;
const int INF = 0x3f3f3f3f; // 表示无穷大
const int MAXN = 100005; // 表示最大顶点数
//图
struct Edge {
int to, w;//to 终点,w距离
Edge(int to, int w) : to(to), w(w) {}
};
int n, m; // n表示顶点数,m表示边数
vector<Edge> G[MAXN]; // vector存储图
int dist[MAXN]; // 存储起点到各顶点的距离
bool vis[MAXN]; // 存储某个点是否已经加入了最短路径树
void dijkstra(int s) {
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
fill(dist + 1, dist + n + 1, INF);
fill(vis + 1, vis + n + 1, false);
dist[s] = 0;
pq.push(make_pair(0, s));
while (!pq.empty()) {
pair<int, int> p = pq.top(); pq.pop();
int v = p.second;
if (vis[v]) continue;
vis[v] = true;
for (Edge e : G[v]) {
if (dist[e.to] > dist[v] + e.w) {//如果e.to到起点的距离大于e.to的前驱节点+
dist[e.to] = dist[v] + e.w;
pq.push(make_pair(dist[e.to], e.to));//第一个值距离,第二个顶点
}
}
}
}
struct Team {
string ID; // 赛队编号
string projectName; // 参赛作品名称
string school; // 参赛学校
string category; // 赛事类别
string members; // 参赛者
string teacher; // 指导老师
};
// 全局变量,存储所有的赛队信息
vector<Team> teams;
// 增加新的赛队信息
void addTeam() {
Team team;
cout << "请输入赛队编号:";
cin >> team.ID;
cout << "请输入参赛作品名称:";
cin >> team.projectName;
cout << "请输入参赛学校:";
cin >> team.school;
cout << "请输入赛事类别:";
cin >> team.category;
cout << "请输入参赛者:";
cin >> team.members;
cout << "请输入指导老师:";
cin >> team.teacher;
teams.push_back(team);
cout << "赛队信息添加成功!" << endl;
}
// 修改指定赛队信息
void modifyTeam() {
string ID;
cout << "请输入需要修改信息的赛队编号:";
cin >> ID;
for (auto& team : teams) {
if (team.ID == ID) {
cout << "请输入新的参赛作品名称:";
cin >> team.projectName;
cout << "请输入新的参赛学校:";
cin >> team.school;
cout << "请输入新的赛事类别:";
cin >> team.category;
cout << "请输入新的参赛者:";
cin >> team.members;
cout << "请输入新的指导老师:";
cin >> team.teacher;
cout << "赛队信息修改成功!" << endl;
return;
}
}
cout << "未找到指定赛队信息!" << endl;
}// 删除指定赛队信息
void deleteTeam() {
string ID;
cout << "请输入需要删除信息的赛队编号:";
cin >> ID;
for (auto it = teams.begin(); it != teams.end(); it++) {
if (it->ID == ID) {
teams.erase(it);
cout << "赛队信息删除成功!" << endl;
return;
}
}
cout << "未找到指定赛队信息!" << endl;
}
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
//基于二叉树排序查找
void insert(TreeNode*& root, int val) {
if (!root) {
root = new TreeNode(val);
return;
}
if (val < root->val) {
insert(root->left, val);
}
else if (val > root->val) {
insert(root->right, val);
}
}
int search(TreeNode* root, int val, int depth) {
if (!root) {
return -1;
}
if (val == root->val) {
return depth;
}
else if (val < root->val) {
return search(root->left, val, depth + 1) + 1;
}
else {
return search(root->right, val, depth + 1) + 1;
}
}
//归并排序查找
void merge(int arr[], int low, int mid, int high) {
int leftSize = mid - low + 1;
int rightSize = high - mid;
int* leftArr = new int[leftSize];
int* rightArr = new int[rightSize];
for (int i = 0; i < leftSize; ++i) {
leftArr[i] = arr[low + i];
}
for (int j = 0; j < rightSize; ++j) {
rightArr[j] = arr[mid + 1 + j];
}
int i = 0, j = 0, k = low;
while (i < leftSize && j < rightSize) {
if (leftArr[i] <= rightArr[j]) {
arr[k++] = leftArr[i++];
}
else {
arr[k++] = rightArr[j++];
}
}
while (i < leftSize) {
arr[k++] = leftArr[i++];
}
while (j < rightSize) {
arr[k++] = rightArr[j++];
}
delete[] leftArr;
delete[] rightArr;
}
// 归并排序函数
void mergeSort(int arr[], int low, int high) {
if (low < high) {
int mid = (low + high) / 2;
mergeSort(arr, low, mid);
mergeSort(arr, mid + 1, high);
merge(arr, low, mid, high);
}
}
// 查找函数(二分法)
int binarySearch(int arr[], int low, int high, int target) {
if (low > high) {
return -1;
}
int mid = (low + high) / 2;
if (arr[mid] == target) {
return mid;
}
else if (arr[mid] < target) {
return binarySearch(arr, mid + 1, high, target);
}
else {
return binarySearch(arr, low, mid - 1, target);
}
}
int main()
{
//读取文件,并将文件中的内容存放到数组当中
int count = 0;
Team team[399];
ifstream fin("C:\\Users\\李一奎\\Documents\\Visual Studio 2010\\Projects\\课设\\team.txt"); // 打开存储数据的文件
if (!fin) {
cout << "文件打开失败!" << endl;
return 0;
}
string line;
while (getline(fin, line)) { // 逐行读取文件内容
int pos = 0; // 记录#出现的位置
int index = 0; // 记录当前读取到的字段
int s = 0;
while (pos != string::npos) { // 在当前行中查找#
pos = line.find('#', index);
string field = line.substr(index, pos - index); // 提取#之前的内容
field.erase(remove(field.begin(), field.end(), '\t'), field.end());
switch (s) { // 根据#出现的位置将提取的内容储存到相应的数组中
case 0:
team[count].ID = field;
s++;
break;
case 1:
team[count].projectName = field;
s++;
break;
case 2:
team[count].school = field;
s++;
break;
case 3:
team[count].category = field;
s++;
break;
case 4:
team[count].members = field;
s++;
break;
case 5:
team[count].teacher = field;
s++;
break;
}
index = pos + 1;
}
count++; // 计数器加1
}
fin.close(); // 关闭文件
for (int i = 1; i < count; i++) {
Team t;
t.ID = team[i].ID;
t.projectName = team[i].projectName;
t.school = team[i].school;
t.category = team[i].category;
t.members = team[i].members;
t.teacher = team[i].teacher;
teams.push_back(t);
}
int a;
do
{
cout << "这是一个赛事管理系统,输入下列编号,即可实现各种功能\n";
cout << "1.管理赛事信息\n\n2.查找赛事信息\n\n3.按学校查询参赛团队\n\n4.决赛叫号系统\n\n5.导航功能\n\n0.退出\n";
cin >> a;
switch (a)
{
case 1: {
int choice;
while (true) {
cout << "请选择操作:1.增加赛队信息 2.修改指定赛队信息 3.删除指定赛队信息 4.退出" << endl;
cin >> choice;
switch (choice) {
case 1:
addTeam();
break;
case 2:
modifyTeam();
break;
case 3:
deleteTeam();
break;
case 4:
cout << "程序已退出!" << endl;
return 0;
default:
cout << "输入有误,请重新输入!" << endl;
break;
}
}
}
break;
case 2: {
int n = 399;
// 建立二叉排序树
int number[500]{};
TreeNode* root = NULL;
for (int i = 1; i < n; i++) {
number[i] = stoi(team[i].ID);
insert(root, number[i]);
}
int val, ch;
cout << "请输入要查找的参赛队编号:";
cin >> val;
for (int i = 0; i < n; i++) {
if (number[i] == val)
ch = i;
}
int depth = 0;
for (int i = 1; i < n; i++) {
number[i] = stoi(team[i].ID);
depth = depth + search(root, number[i], 1);
}
if (depth == -1) {
cout << "查找失败!" << endl;
}
else {//参赛队编号,参赛作品名称,参赛学校,赛事类别,参赛者,指导老师
cout << "参赛队编号为:" << val << endl;
cout << "参赛作品名称:" << team[ch].projectName << endl;
cout << "参赛学校:" << team[ch].school << endl;
cout << "赛事类别:" << team[ch].category << endl;
cout << "参赛者:" << team[ch].members << endl;
cout << "指导老师:" << team[ch].teacher << endl;
cout << "平均查找长度ASL为:" << (double)depth / (n-1) << endl;
}
break;
}
case 3: {
string school_name;
cout << "请输入学校名称:";
cin >> school_name;
int n = 399;
int count = 0;
for (int i = 0; i < n - 1; i++) {
if (teams[i].school.compare(school_name) == 0) {
count++;
}
}
if (count == 0) {
cout << "未找到该学校的参赛团队!" << endl;
}
else {
int* found_teams = new int[count];
int index = 0;
for (int i = 0; i < n - 1; i++) {
if (teams[i].school.compare(school_name) == 0) {
found_teams[index] = stoi(teams[i].ID);
index++;
}
}
mergeSort(found_teams, 0, count - 1);
cout << "参赛队编号\t" << "参赛作品名称\t" << "参赛学校\t" << "赛事类别\t" << "参赛者\t" << "指导老师\t\n";
for (int i = 0; i < count; i++) {
for (int j = 0; j < 398; j++) {
if (found_teams[i] == stoi(teams[j].ID)) {
cout << teams[j].ID << "\t";
cout << teams[j].projectName << "\t";
cout << teams[j].school << "\t";
cout << teams[j].category << "\t";
cout << teams[j].members << "\t";
cout << teams[j].teacher << endl;
}
}
}
delete[] found_teams;
}
}
break;
case 4: {
int arr[398] = { 0 };
for (int i = 0; i < 398; i++) {
arr[i] = stoi(teams[i].ID);
}; // 假设已有400组编号被存储在arr数组中
queue<int> q; // 定义一个循环队列,最大容量为9
int index = 0; // 当前处理的队伍编号在arr数组中的下标
while (index < 398 || !q.empty()) { // 比较队伍编号数组的长度和循环队列是否都为空
if (q.size() == 9) { // 计时器时间到达队伍进入时间而且队列中有9个队伍
cout << "比赛开始:"<< endl;// 输出提示信息
for (int i = 0; i < 9; ++i) { // 取出队首的9个队伍进行比赛
cout << q.front() << "\n";
q.pop();
}
cout << endl; // 输出换行符
this_thread::sleep_for(chrono::milliseconds(500));
}
if (index < 398) { // 队伍编号数组还有剩余队伍
q.push(arr[index++]); // 将当前处理的队伍编号添加到队列
}
this_thread::yield(); // 等待其他程序运行
}
}
break;
case 5: {
// 读入n,m和边集E
n = 10;
m = 14;
G[1].push_back(Edge(2, 100));
G[1].push_back(Edge(4, 200));
G[2].push_back(Edge(1, 100));
G[2].push_back(Edge(3, 80));
G[2].push_back(Edge(4, 150));
G[3].push_back(Edge(2, 80));
G[3].push_back(Edge(5, 120));
G[3].push_back(Edge(6, 110));
G[4].push_back(Edge(1, 200));
G[4].push_back(Edge(2, 150));
G[4].push_back(Edge(5, 50));
G[5].push_back(Edge(3, 120));
G[5].push_back(Edge(4, 50));
G[5].push_back(Edge(8, 150));
G[5].push_back(Edge(9, 230));
G[6].push_back(Edge(3, 110));
G[6].push_back(Edge(7, 80));
G[6].push_back(Edge(8, 60));
G[7].push_back(Edge(6, 80));
G[7].push_back(Edge(10, 100));
G[8].push_back(Edge(5, 150));
G[8].push_back(Edge(9, 90));
G[8].push_back(Edge(6, 60));
G[8].push_back(Edge(10, 70));
G[9].push_back(Edge(5, 230));
G[9].push_back(Edge(8, 90));
G[9].push_back(Edge(10, 50));
G[10].push_back(Edge(7, 100));
G[10].push_back(Edge(8, 70));
G[10].push_back(Edge(9, 50));
// 输入起点和终点
int start, end;
cout << "请输入起点和终点:";
cin >> start >> end;
// 运行Dijkstra算法
dijkstra(start);
// 输出结果
cout << "最短路径的途径编号为:";
vector<int> path;
int cur = end;
path.push_back(cur);
while (cur != start) {
for (Edge e : G[cur]) {
if (dist[e.to] + e.w == dist[cur]) {
cur = e.to;
path.push_back(cur);
break;
}
}
}
for (int i = path.size() - 1; i >= 0; --i) {
cout << path[i];
if (i != 0) cout << "->";
}
cout << endl;
cout << "最短路径的距离之和为:" << dist[end] << endl;
}
case 0: {
// system("cls");
}
}
} while (a != 0);
return 0;
}