目录
项目1:计算机设计大赛赛事统计
一、设计主要要求
【问题描述】
参加计算机设计大赛的n个学校编号为1~n,赛事分成m个项目,项目的编号为1~m.比赛获奖按照得分降序,取前三名,写一个统计程序产生各种成绩单和得分报表。
【基本要求】
1)每个比赛项目至少有10支参赛队;每个学校最多有6支队伍参赛;
2)能统计各学校的总分;
3)可以按照学校编号或名称,学校的总分、各项目的总分排序输出;
4)可以按学校编号查询学校某个项目的获奖情况;可以按项目编号查询取得前三名的学校;
5)数据存入文件并能随时查询
【设计要求】
1)输入数据形式和范围:可以输入学校的名称,赛事项目的名称。
2)输出形式:有中文提示,各学校分数为整数
3)界面要求:交互设计要合理,每个功能可以设立菜单,根据提示,可以完成相关功能的要求。
4)存储结构:学生自己根据系统功能要求自己设计,但是赛事相关数据要存储在文件中。
【测试数据】
要求使用全部合法数据,整体非法数据,局部非法数据。进行程序测试,以保证程序的稳定。
【实现提示】
假设3<赛事项目数量<=10,学校名称长度不超过20个字符。每个赛事结束时,将其编号、名称输入,并依次输入参赛学校编号、学校名称和成绩。
二.问题分析和任务定义
本程序需要实现的功能有:合理的存储、文件存取、用户界面、增加数据功能、删减修改数据功能、统计各个学校总分功能、可以按照学校总分输出成绩表、按照学校编号查询项目获奖情况。可以分解为以下小模块:
1.学校信息管理
2.赛事信息管理
3.队伍报名管理
4.查看获奖信息
5.总分排行榜
6.获奖排行榜
7.关于本程序
0.保存并退出
层次可以表示如下
void menu(); //一级界面(主界面)
void manageSchool(); //二级界面1:学校信息管理
void manageContest(); //二级界面2:比赛信息管理
void teamRegistration(); //二级界面3:队伍报名管理
void viewPrize(); //二级界面4:查询学校获奖信息
void printScoreRank(); //二级界面5:总分排行榜
void printPrizeRank(); //二级界面6:获奖排行榜
void printInformation(); //二级界面7:程序信息
限制条件注意点:赛事项目数量限制、赛事参赛队伍最少数量限制,非法输入处理......
三.逻辑设计
【运行时存储结构】
(1)Team类
ADT Team
Data
编号,属于哪个学校,报名哪个比赛,成绩
Operation
Constructor
初始数据:编号,学校,竞赛
功能:初始化编号、隶属学校、隶属竞赛,成绩默认为0
Constructor
初始数据:编号,学校,竞赛,成绩
功能:初始化编号、隶属学校、隶属竞赛,成绩
getScore
前提条件:无
输入:无
功能:访问并返回私有变量成绩的数值
输出:成绩
后置条件:无
getNoble
前提条件:无
输入:无
功能:访问并返回私有变量编号的数值
输出:编号
后置条件:无
getSchool
前提条件:无
输入:无
功能:返回隶属学校的数值
输出:隶属学校的编号
后置条件:无
getContest
前提条件:无
输入:无
功能:返回隶属竞赛的数值
输出:隶属竞赛的编号
后置条件:无
setScore
前提条件:无
输入:要设置的成绩
功能:设置私有变量成绩
输出:无
后置条件:无
endADT
(2)School
ADT School {
Data
编号,名称,队伍数量,队伍编号数组,总成绩
Operation
Constructor
初始数据:编号,学校名称
功能:初始化编号、学校名称
addTeam
前提条件:队伍未满
输入:队伍编号
功能:将该编号的队伍加入学校的队伍数组内,并将队伍数量+1
输出:无
后置条件:无
getNoble
前提条件:无
输入:无
功能:访问并返回私有变量编号的数值
输出:编号
后置条件:无
getName
前提条件:无
输入:无
功能:返回学校的名称
输出:学校名字
后置条件:无
getNum
前提条件:无
输入:无
功能:返回下辖队伍数量
输出:队伍数量
后置条件:无
getNum
前提条件:下标<队伍数量
输入:下标
功能:返回下标对应的队伍编号
输出:下标的队伍编号
后置条件:无
Sum
前提条件:无
输入:无
功能:计算队伍的总分
输出:无
后置条件:无
getSum
前提条件:已经调用过Sum计算总分
输入:无
功能:返回计算好的队伍总分
输出:总分
后置条件:无
endADT
(3)Contest类
ADT School {
Data
编号,名称,队伍数量,参赛队伍编号数组,是否结束标志
Operation
Constructor
初始数据:编号,竞赛名称
功能:初始化编号、竞赛名称
addTeam
前提条件:无
输入:队伍编号
功能:将该编号的队伍加入竞赛的队伍数组内,并将队伍数量+1
输出:无
后置条件:无
getNoble
前提条件:无
输入:无
功能:访问并返回私有变量编号的数值
输出:编号
后置条件:无
getName
前提条件:无
输入:无
功能:返回竞赛的名称
输出:学校名字
后置条件:无
getNum
前提条件:无
输入:无
功能:返回报名参加的队伍数量
输出:队伍数量
后置条件:无
getNum
前提条件:下标<队伍数量
输入:下标
功能:返回下标对应的队伍编号
输出:下标的队伍编号
后置条件:无
rank
前提条件:已调用过finish函数
输入:无
功能:将队伍按照分数进行排序
输出:无
后置条件:无
finish
前提条件:队伍数>=10,并都已打分
输入:无
功能:将比赛状态设置为完成
输出:无
后置条件:无
isFinished
前提条件:无
输入:无
功能:返回比赛是否完成
输出:比赛完成与否
后置条件:无
endADT
私有变量:赛事编号、赛事名称、含有队伍数、还有队伍编号(数组)等。
公有函数:构造函数、增加队伍函数、排序函数、输出获奖信息函数等。
(4)teams数组、schools数组、contest数组
用标准容器vector存储,基本逻辑为按照学校编号保持有序(便于插入、查询时减小时间复杂度)
【文件中存储格式】
第一行三个数A,B,C分别表示:学校个数、赛事个数、队伍数量。
接下来A行:编号、校名。
接下来B行:编号、赛事状态(结束与否)、赛事名。
接下来C行:编号、参加赛事编号、隶属学校编号、分数。
【主要功能函数】
int findSchool(int noble){ //功能函数1:寻找学校,返回下标
int findContest(int noble){ //功能函数2:寻找赛事,返回下标
int findTeam(int noble){ //功能函数3:找到编号为noble的队伍在容器中的下标,找不到返回-1
void printContestList(){ //功能函数4:输出比赛列表
int MaxOfTeamNoble(){ //功能函数5:返回队伍编号最大值
void insertTeam(Team x){ //功能函数6:将新的Team对象按照编号大小顺序加入teams
void insertContest(Contest x){ //功能函数7:将新的Contest对象按照编号大小顺序加入contests
void insertSchool(School x){ //功能函数8:将新的School对象按照编号大小顺序加入schools
void writeToFile(){ //功能函数9:保存数据进入文档
void readFromFile(){ //功能函数10:从文档中读取
void firstUse(){ //功能函数11:首次使用
void manageSchool(){ //二级界面1:学校信息管理
void manageContest(){ //二级界面2:比赛信息管理
...
【调用关系图】
注:在各个函数中都可按需调用功能函数,在图不再列出
【流程图】
四、物理设计
以下列出一些数据结构与功能函数。
(1)Team类结构
class Team {
public:
Team(int n, int school) {
this->n = n;
this->belongToSchool = school;
}
Team(int n, int school, int contest) {
this->n = n;
belongToContest = contest;
belongToSchool = school;
score = 0;
}
Team(int n, int school, int contest, int score) {
this->n = n;
belongToContest = contest;
belongToSchool = school;
this->score = score;
}
int getScore() { return score; }
int getNoble() { return n; }
int getSchool() { return belongToSchool; }
int getContest() { return belongToContest; }
void setScore(int score) {
this->score = score;
}
private:
int score;
int n;
int belongToContest;
int belongToSchool;
};
(2)School类结构
class School {
public:
School() {}
School(int n, char* name) {
this->n = n;
this->name = name;
team = 0;
for (int i = 0; i < 6; i++) teamNum[i] = 0;
sumScore = 0;
}
void addTeam(int num) { //加入队伍
if (team >= 6) {
cout << "队伍数已满\n";
return;
}
teamNum[team++] = num;
}
void Sum(); //求和,可返回分数和,并且将结果储存在sumScore中
int getSum() {
return sumScore;
}
int getNoble() { return n; }
string getName() { return name; }
int getNum() { return team; }
int getNum(int i) {
if (i < team) return teamNum[i];
}
private:
int n;
string name;
int team;
int teamNum[6];
int sumScore;
};
(3)Contest类结构
class Contest {
public:
Contest(int n, char* name) {
team = 0;
this->n = n;
this->name = name;
finished = false;
}
void addTeam(int num) {
teamNum[team++] = num;
}
void finish() { finished = true; }
bool isFinished() { return finished; }
void rank() { //排序
sort(teamNum, teamNum + team, cmpByScore);
}
int getNoble() { return n; }
string getName() { return name; }
int getNum() { return team; }
int getNum(int i) { return teamNum[i]; }
private:
bool finished;
int n;
string name;
int team;
int teamNum[N]; //存放参赛队伍
};
(4)其他数据结构
vector<Team> teams;
vector<Contest> contests;
vector<School> schools;
int A,B,C; //A:学校数量 B:比赛数量 C:队伍数量
(5)文件读写
void writeToFile() { //功能函数9:保存数据进入文档
fstream out;
out.open("data.txt", ios::out | ios::ate);
out << A << ' ' << B << ' ' << C << '\n';
for (int i = 0; i < A; i++) {
out << schools[i].getNoble() << schools[i].getName() << "\n";
}
for (int i = 0; i < B; i++) {
out << contests[i].getNoble() << " " << (int)contests[i].isFinished() << contests[i].getName() << "\n";
}
for (int i = 0; i < C; i++) {
out << teams[i].getNoble() << " " << teams[i].getSchool() << " " << teams[i].getContest() << " " << teams[i].getScore() << "\n";
}
out.close();
}
void readFromFile() { //功能函数10:从文档中读取
fstream in;
in.open("data.txt", ios::in);
in >> A >> B >> C;
for (int i = 0; i < A; i++) {
int noble;
char s[20];
in >> noble;
in.getline(s, 20, '\n');
School x(noble, s);
insertSchool(x);
}
for (int i = 0; i < B; i++) {
int noble;
char s[20];
in >> noble;
int finished = 0;
in >> finished;
in.getline(s, 20, '\n');
Contest x(noble, s);
if (finished == 1) x.finish();
insertContest(x);
}
for (int i = 0; i < C; i++) {
int noble;
int belongC;
int belongS;
int score;
in >> noble >> belongS >> belongC >> score;
Team x(noble, belongS, belongC, score);
insertTeam(x);
schools[findSchool(belongS)].addTeam(noble); //调用学校类函数addTeam,将本队加入学校数据中
contests[findContest(belongC)].addTeam(noble); //更新竞赛队伍数据
}
in.close();
cout << "读取存档成功!";
}
(6)find函数
int findSchool(int noble) { //功能函数1:寻找学校,返回下标
vector<School>::iterator i;
int location = 0;
for (i = schools.begin(); i != schools.end(); i++) {
if (i->getNoble() == noble) return location;
else if (i->getNoble() > noble) return -1;
else location++;
}
return -1;
}
int findContest(int noble) { //功能函数2:寻找赛事,返回下标
vector<Contest>::iterator i;
int location = 0;
for (i = contests.begin(); i != contests.end(); i++) {
if (i->getNoble() == noble) return location;
else if (i->getNoble() > noble) return -1;
else location++;
}
return -1;
}
int findTeam(int noble) { //功能函数3:找到编号为noble的队伍在容器中的下标,找不到返回-1
vector<Team>::iterator i;
int location = 0;
for (i = teams.begin(); i != teams.end(); i++) {
if (i->getNoble() == noble) return location;
else if (i->getNoble() > noble) return -1;
else location++;
}
return -1;
}
(7)print函数
void printContestList() { //功能函数4:输出比赛列表
if (contests.empty()) cout << "当前赛事列表为空!\n";
vector<Contest>::iterator it;
for (it = contests.begin(); it != contests.end(); it++) {
cout << "编号" << it->getNoble() << " " << it->getName() << "\t已报名队伍数量:" << it->getNum() << "\t";
if (it->isFinished()) cout << "已结束\n";
else cout << "尚未结束\n";
}
}
void printSchoolList() { //功能函数12:输出学校列表
cout << "当前已有学校:\n";
vector<School>::iterator it;
for (it = schools.begin(); it != schools.end(); it++)
cout << "编号" << it->getNoble() << " " << it->getName() << "\t队伍数量:" << it->getNum() << "\n";
}
void printTeamList() { //功能函数13:输出队伍列表
cout << "当前已有队伍" << teams.size() << "支\n";
vector<Team>::iterator it;
for (it = teams.begin(); it != teams.end(); it++)
cout << "编号" << it->getNoble() << " 隶属于" << schools[findSchool(it->getSchool())].getName() << "参加了"<<contests[findContest(it->getContest())].getName()<< endl;
}
(8)insert函数
void insertTeam(Team x) { //功能函数6:将新的Team对象按照编号大小顺序加入teams
if (findTeam(x.getNoble()) != -1) {
cout << "该编号的队伍已经存在";
return;
}
else {
vector<Team>::iterator i;
bool flag = false; //标记是否在中间插入过,如果没有,说明应该插在尾部
for (i = teams.begin(); i != teams.end(); i++) {
if (i->getNoble() > x.getNoble()) { //第一个编号比x更大的,插入在此处之前
flag = true; //标记更新
teams.insert(i, x); //插入
break; //找到了即退出
} //中间没有合适位置,直接push_back到尾部
}
if (!flag) teams.push_back(x);
}
}
void insertContest(Contest x) { //功能函数7:将新的Contest对象按照编号大小顺序加入contests
if (findContest(x.getNoble()) != -1) {
cout << "该编号的竞赛已经存在";
return;
}
else {
vector<Contest>::iterator i;
bool flag = false; //标记是否在中间插入过,如果没有,说明应该插在尾部
for (i = contests.begin(); i != contests.end(); i++) {
if (i->getNoble() > x.getNoble()) { //第一个编号比x更大的,插入在此处之前
flag = true; //标记更新
contests.insert(i, x); //插入
break; //找到了即退出
}
}
if (!flag) contests.push_back(x); //中间没有合适位置,直接push_back到尾部
}
}
void insertSchool(School x) { //功能函数8:将新的School对象按照编号大小顺序加入schools
if (findSchool(x.getNoble()) != -1) {
cout << "该编号的学校已经存在";
return;
}
else {
vector<School>::iterator i;
bool flag = false; //标记是否在中间插入过,如果没有,说明应该插在尾部
for (i = schools.begin(); i != schools.end(); i++) {
if (i->getNoble() > x.getNoble()) { //第一个编号比x更大的,插入在此处之前
flag = true; //标记更新
schools.insert(i, x); //插入
break; //找到了即退出
}
}
if (!flag) schools.push_back(x); //中间没有合适位置,直接push_back到尾部
}
}
(9)界面函数
void manageSchool() { //二级界面1:学校信息管理
char choice;
while (true) {
cout << "***********学校信息编辑***********\n";
cout << "1.输出全部学校信息\n";
cout << "2.新加入学校信息\n";
cout << "3.删除学校\n";
cout << "0.返回\n";
cin >> choice;
if (choice == '1') {
if (schools.empty()) cout << "当前学校列表为空!\n";
vector<School>::iterator it;
for (it = schools.begin(); it != schools.end(); it++)
cout << "编号" << it->getNoble() << " " << it->getName() << " 含有队伍数量:" << it->getNum() << "\n";
}
else if (choice == '2') {
cout << "请输入学校编号:";
int noble;
cin.clear();
cin.ignore();
cin >> noble;
if (findSchool(noble) != -1) cout << "已有该编号的学校,录入失败!\n";
else if (noble <= 0)
cout << "必须输入正整数!\n";
else {
cout << "请输入学校名称:";
char s[50];
cin >> s;
School newAdd(noble, s);
A++;
insertSchool(newAdd);
}
}
else if (choice == '3') {
cout << "您要删除的学校编号:";
int noble;
cin.clear();
cin.ignore();
cin >> noble;
if (findSchool(noble) == -1) cout << "该学校编号不存在,请重新输入\n";
else {
School& del = schools[findSchool(noble)];
C -= del.getNum();
for (int i = 0; i < del.getNum(); i++) {
cout << "该学校编号为" << del.getNum(i) << "的队伍已被删除\n";
teams.erase(teams.begin() + findTeam(del.getNum(i)));
}
cout << "编号为:" << noble << "的" << schools[findSchool(noble)].getName() << "已被成功删除!\n";
schools.erase(schools.begin() + findSchool(noble));
A--;
}
}
else if (choice == '0') { writeToFile(); break; }
}
}
void manageContest() { //二级界面2:比赛信息管理
char choice;
while (true) {
cout << "***********赛事信息编辑***********\n";
cout << "1.输出全部赛事信息\n";
cout << "2.新加入赛事\n";
cout << "3.队伍打分\n";
cout << "4.查看比赛结果\n";
cout << "0.返回\n";
cin >> choice;
if (choice == '1') {
printContestList();
}
else if (choice == '2') {
cout << "请输入赛事编号:";
int noble;
cin.clear();
cin.ignore();
cin >> noble;
if (findContest(noble) != -1) cout << "已有该编号的比赛,录入失败!\n";
else if (noble <= 0)
cout << "必须输入正整数!\n";
else {
cout << "请输入赛事名称:";
char s[20];
cin >> s;
Contest newAdd(noble, s);
B++;
insertContest(newAdd);
cout << "加入成功!\n";
}
}
else if (choice == '3') {
printContestList();
cout << "输入要进行打分的比赛编号:";
int noble;
cin.clear();
cin.ignore();
cin >> noble;
if (findContest(noble) == -1) cout << "比赛不存在!\n";
else {
Contest& now = contests[findContest(noble)];
if (now.getNum() < 10) {
cout << "报名队伍过少,比赛不能开始\n";
continue;
}
else if (now.isFinished()) {
cout << "该比赛已打分\n";
continue;
}
for (int i = 0; i < now.getNum(); i++) {
Team& nowTeam = teams[findTeam(now.getNum(i))];
cout << "请给" << i + 1 << "号队伍(编号" << now.getNum(i) << "," << schools[findSchool(nowTeam.getSchool())].getName() << ")打分:";
int score;
cin >> score;
nowTeam.setScore(score);
}
now.finish();
now.rank();
}
}
else if (choice == '4') {
printContestList();
cout << "要查看那一场比赛结果:";
int noble;
cin.clear();
cin.ignore();
cin >> noble;
if (findContest(noble) == -1) cout << "比赛不存在!\n";
else {
Contest& now = contests[findContest(noble)];
if (!now.isFinished())
cout << "尚未打分,无法查看结果!\n";
else {
now.rank();
for (int i = 0; i < now.getNum(); i++) {
Team& nowTeam = teams[findTeam(now.getNum(i))];
cout << "No." << i + 1 << " " << schools[findSchool(nowTeam.getSchool())].getName() << " 分数:" << nowTeam.getScore() << "\n";
}
}
}
}
else if (choice == '0') { writeToFile(); break; }
}
}
void teamRegistration() { //二级界面3:队伍报名管理
char choice;
while (true) {
cout << "***********队伍报名管理***********\n";
cout << "1.学校批量报名\n";
cout << "2.新建一支队伍\n";
cout << "3.查看所有队伍状态\n";
cout << "0.返回\n";
cin >> choice;
if (choice == '1') {
printSchoolList();
cout << "请输入学校编号:";
int noble;
cin.clear();
cin.ignore();
cin >> noble;
if (findSchool(noble) == -1) cout << "该编号的学校不存在!\n";
else {
School& nowSchool = schools[findSchool(noble)];
int left = 6 - nowSchool.getNum();
cout << "要报名的队伍数(剩余容量" << left << "):";
int num;
cin >> num;
if (num > left) {
cout << "名额不足!\n";
continue;
}
else if (num < 0) {
cout << "必须输入正整数\n";
continue;
}
else {
printContestList();
int i = 0;
while (i < num) {
cout << "队伍" << i + 1 << "报名的比赛编号:";
int test;
cin.clear();
cin.sync();
cin >> test;
if (findContest(test) == -1) {
cout << "报名的比赛不存在,请重新输入\n";
continue;
}
Contest& nowContest = contests[findContest(test)];
Team newTeam(MaxOfTeamNoble() + 1, noble, test);
C++; i++;
cout << "报名成功!\n";
if (nowContest.isFinished()) {
cout << "当前比赛已开始,请输入该队伍成绩:";
int score;
cin.clear();
cin.sync();
cin >> score;
newTeam.setScore(score);
nowContest.rank();
}
insertTeam(newTeam);
nowSchool.addTeam(newTeam.getNoble());
nowContest.addTeam(newTeam.getNoble());
}
}
}
}
else if (choice == '2') {
printSchoolList();
cout << "该队隶属学校:";
int noble;
cin.clear();
cin.ignore();
cin >> noble;
if (findSchool(noble) == -1) cout << "该学校不存在!\n";
else {
School& nowSchool = schools[findSchool(noble)];
int left = 6 - nowSchool.getNum();
if (left == 0) {
cout << "该学校队伍已满\n";
continue;
}
printContestList();
cout << "报名的比赛编号:";
int test;
cin.clear();
cin.sync();
cin >> test;
if (findContest(test) == -1) {
cout << "报名的比赛不存在\n";
continue;
}
Contest& nowContest = contests[findContest(test)];
Team newTeam(MaxOfTeamNoble() + 1, noble, test);
C++;
cout << "报名成功!\n";
if (nowContest.isFinished()) {
cout << "当前比赛已开始,请输入该队伍成绩:";
int score;
cin.clear();
cin.sync();
cin >> score;
newTeam.setScore(score);
nowContest.rank();
}
insertTeam(newTeam);
nowSchool.addTeam(newTeam.getNoble());
nowContest.addTeam(newTeam.getNoble());
}
}
else if (choice == '3') {
if (teams.empty())
cout << "当前队伍为空\n";
else printTeamList();
}
else if (choice == '0') {
writeToFile();
return;
}
}
}
void viewPrize() { //二级界面4:查询学校获奖信息
char choice;
while (true) {
cout << "*************获奖查询*************\n";
cout << "1.查询学校获奖信息\n";
cout << "2.查询竞赛获奖信息\n";
cout << "0.退出\n";
cin >> choice;
if (choice == '1') {
printSchoolList();
cout << "请输入学校编号:";
int noble;
cin.clear();
cin.ignore();
cin >> noble;
if (findSchool(noble) == -1) {
cout << "该学校不存在\n";
}
else {
School& nowSchool = schools[findSchool(noble)];
bool isFind = false;
for (int i = 0; i < nowSchool.getNum(); i++) { //遍历该学校的每一支队伍
Team& nowTeam = teams[findTeam(nowSchool.getNum(i))];
Contest& nowContest = contests[findContest(nowTeam.getContest())];
nowContest.rank();
if (nowContest.isFinished()) { //查找前三中是否有该队伍
int prize=1;
int i;
for (i = 0; i < 3; i++) {
if(i==0) prize=1;
else if(teams[findTeam(nowContest.getNum(i))].getScore()!=teams[findTeam(nowContest.getNum(i-1))].getScore())
prize=i+1;
//考虑到分数一样奖牌也应该一样的情况
if (nowContest.getNum(i) == nowTeam.getNoble()) {
cout << nowSchool.getName() << "的队伍在编号为" << nowContest.getNoble() << "的" << nowContest.getName() << "比赛中,获得了" << prize << "等奖\n";
isFind = true;
}
}
//考虑后续还有分数相平的情况
while(teams[findTeam(nowContest.getNum(i))].getScore()==teams[findTeam(nowContest.getNum(i-1))].getScore()&&i<nowContest.getNum()){
if (nowContest.getNum(i) == nowTeam.getNoble()) {
cout << nowSchool.getName() << "的队伍在编号为" << nowContest.getNoble() << "的" << nowContest.getName() << "比赛中,获得了" << prize << "等奖\n";
isFind = true;
}
i++;
}
}
}
if (!isFind) cout << "无获奖信息\n";
}
}
else if (choice == '2') {
printContestList();
cout << "请输入比赛编号:";
int noble;
cin.clear();
cin.ignore();
cin >> noble;
if (findContest(noble) == -1) {
cout << "该比赛不存在\n";
}
else {
Contest& now = contests[findContest(noble)];
now.rank();
if (!now.isFinished())
cout << "该比赛尚未结束\n";
else {
int prize=1;
int i;
for (i = 0; i < 3; i++) {
if(i==0) prize=1;
else if(teams[findTeam(now.getNum(i))].getScore()!=teams[findTeam(now.getNum(i-1))].getScore())
prize=i+1;
Team& nowTeam = teams[findTeam(now.getNum(i))];
cout << prize << "等奖:" << schools[findSchool(nowTeam.getSchool())].getName() << "的队伍(编号" << nowTeam.getNoble() << ") 分数为:" << nowTeam.getScore() << endl;
}
while(teams[findTeam(now.getNum(i))].getScore()==teams[findTeam(now.getNum(i-1))].getScore()&&i<now.getNum()){
Team& nowTeam = teams[findTeam(now.getNum(i))];
cout << prize << "等奖:" << schools[findSchool(nowTeam.getSchool())].getName() << "的队伍(编号" << nowTeam.getNoble() << ") 分数为:" << nowTeam.getScore() << endl;
i++;
}
}
}
}
else if (choice == '0') {
writeToFile();
return;
}
}
}
void printScoreRank() { //二级界面5:总分排行榜
cout << "************总分排行榜************\n";
int m = schools.size();
int* rankList = new int[m];
for (int i = 0; i < m; i++) {
rankList[i] = i;
schools[i].Sum();
}
sort(rankList, rankList + m, cmpBySum);
int prize;
for (int i = 0; i < m; i++) {
if(i==0) prize=1;
else if(schools[rankList[i]].getSum()!=schools[rankList[i-1]].getSum())
prize=i+1;
cout << "No." << prize << "\t" << schools[rankList[i]].getName();
//for (int j = 0; j < 20 - (schools[rankList[i]].getName()).length(); j++) cout <<" ";
cout << "(" << schools[rankList[i]].getSum() << ")\n";
}
delete[] rankList;
}
void printPrizeRank() { //二级界面6:获奖排行榜
cout << "************奖牌排行榜************\n";
cout << "名次\t金\t银\t铜\t编号\t 学校名称\t\n";
int m = schools.size();
int* gold = new int[m];
int* silver = new int[m];
int* bronze = new int[m];
for (int i = 0; i < m; i++) gold[i] = silver[i] = bronze[i] = 0;
int* rankList = new int[m];
for(int i=0;i<B;i++){
contests[i].rank();
}
for(int i=0;i<A;i++){
schools[i].Sum();
}
for (int i = 0; i < m; i++) {
School& nowSchool = schools[i];
for (int j = 0; j < nowSchool.getNum(); j++) { //遍历该学校的每一支队伍
Team& nowTeam = teams[findTeam(nowSchool.getNum(j))];
Contest& nowContest = contests[findContest(nowTeam.getContest())];
if (nowContest.isFinished()) { //查找前三中是否有该队伍
if (nowContest.getNum(0) == nowTeam.getNoble()){
gold[i]++;
}
if (nowContest.getNum(1) == nowTeam.getNoble())
silver[i]++;
if (nowContest.getNum(2) == nowTeam.getNoble())
bronze[i]++;
}
}
rankList[i] = i;
}
for (int i = 0; i < m - 1; i++)
for (int j = i + 1; j < m; j++) {
bool swap = false;
if (gold[rankList[i]] < gold[rankList[j]])
swap = true;
else if (gold[rankList[i]] == gold[rankList[j]])
if (silver[rankList[i]] < silver[rankList[j]])
swap = true;
else if (silver[rankList[i]] == silver[rankList[j]])
if (bronze[rankList[i]] < bronze[rankList[j]])
swap = true;
else if(bronze[rankList[i]] == bronze[rankList[j]])
if(schools[rankList[i]].getSum()<schools[rankList[j]].getSum())
swap=true;
if (swap) {
int t = rankList[i];
rankList[i] = rankList[j];
rankList[j] = t;
}
}
for (int i = 0; i < m; i++) {
cout << "No." << i + 1 << "\t"<<gold[rankList[i]] << "\t" << silver[rankList[i]] << "\t" << bronze[rankList[i]];
cout << "\t" << schools[rankList[i]].getNoble() << "\t" << schools[rankList[i]].getName()<<"\n";
}
delete[] gold;
delete[] silver;
delete[] bronze;
delete[] rankList;
}
void printInformation() { //二级界面7:程序信息
cout << "************程序版本信息************\n";
cout << "此程序为Jayden Wang 独立自主设计\n";
cout << "用作数据结构项目设计\n";
cout << "更新日志:\n2022.5.17 程序框架搭建完成\n";
cout << "2022.5.19 发布 Vision 1.0.0 Beta.\n";
cout << "2022.5.19晚 v1.1.0 \n\t修复了mac端输出成绩表时出现死循环的问题;修复了每读一次文档,学校与比赛名称前多一个空格的问题。\n ";
cout << "2022.5.19晚 v1.1.1\n\t修复了模块5输出不正确的bug,输出了模块6输出不正确的bug\n";
cout << "2022.5.24 v1.1.2\n\t修复了在查询学校获奖信息时,因为未调用排序rank()算法而产生的错误\n";
cout << "2022.5.25 v1.2.1\n\t界面美化、加入了新功能:并列学校相同奖项算法,例如:如果有两个第一名,则奖项分别为1 1 3等奖,如果第三名与第四名分数相同,则奖项为1 2 3 3,以此类推\n";
}
void menu() { //一级界面(主界面)
cout << "*********欢迎使用管理系统*********\n";
fstream file;
file.open("data.txt", ios::in);
if (!file.is_open()) {
cout << "未找到数据文件,本次为首次使用,请根据提示录入数据.\n";
file.close();
firstUse();
}
else {
cout << "已找到存档\n";
file.close();
readFromFile();
}
char choice = ' ';
while (choice != 7) {
cout << "\n**************操作****************\n";
cout << "1.学校信息管理\n";
cout << "2.赛事信息管理\n";
cout << "3.队伍报名管理\n";
cout << "4.查看获奖信息\n";
cout << "5.总分排行榜\n";
cout << "6.获奖排行榜\n";
cout << "7.关于本程序\n";
cout << "0.保存并退出\n";
cin >> choice;
switch (choice) {
case '1':manageSchool(); break;
case '2':manageContest(); break;
case '3':teamRegistration(); break;
case '4':viewPrize(); break;
case '5':printScoreRank(); break;
case '6':printPrizeRank(); break;
case '7':printInformation(); break;
case '0':writeToFile(); return;
default:break;
}
}
}
五、测试数据
data.txt储存示例(数据为随意编造,与对应大学实际水平无关)
8 4 44 //A,B,C
1 江苏科技大学 //参赛学校数据
2 苏州科技大学
3 江苏大学
4 浙江大学
5 北京大学
6 清华大学
7 中国科学技术大学
8 武汉大学
1 1 C++ //比赛项目数据
2 1 JAVA
3 1 PYTHON
4 0 C#
1 1 1 90 //队伍数据
2 1 2 99
3 1 3 98
4 1 4 0
5 2 3 95
6 2 1 80
7 3 1 79
8 3 1 69
9 3 1 88
10 3 1 91
11 3 1 92
12 4 1 95
13 4 1 91
14 4 2 98
15 4 2 96
16 4 2 94
17 4 3 92
18 2 1 24
19 2 1 99
20 2 2 92
21 1 2 88
22 1 3 94
23 5 2 89
24 5 3 100
25 5 2 92
26 5 3 92
27 5 2 94
28 5 4 0
29 6 2 99
30 6 3 93
31 7 3 97
32 7 4 0
33 7 4 0
34 7 4 0
35 7 4 0
36 7 3 10
37 8 4 0
38 8 3 99
39 8 4 0
40 8 4 0
41 6 4 0
42 6 4 0
43 6 2 100
44 8 3 8
补充
本程序得益于开始的精心数据结构设计,预留了很多升级空间:比如单个修改删除队伍数据、删除学校包括其下辖队伍(这个其实已经做了,做完了才发现没有要求),删除比赛以及内含队伍,来达到更逼真地模仿一般的管理系统;本程序的寻址通过find函数实现,插入使用insert函数实现,与单纯的数组下标寻址相比,对于编号的连续性其实没有依赖,也不需要事先规定学校比赛数量
——但是笔者是在是太忙碌着对付期末考试(lan)了,所以......这样也够用了。
项目2:校园导游咨询
一、设计主要要求
【问题描述】
设计一个校园导游程序,为来访的客人提供各种信息查询服务。
【基本要求】
(1) 设计你所在学校的校园平面图,所含景点不少于10个.以图中顶点表示校内各景点,存放景点名称、代号、简介 等信息;以边表示路径,存放路径长度等相关信息。
(2) 为来访客人提供图中任意景点相关信息的查询。
(3) 为来访客人提供图中任意景点的问路查询,即查询任意两个景点之间的一条最短的简单路径。
【测试数据】
以江苏科技大学长山校区为例。
【实现提示】
一般情况下,校园的道路是双向通行的,可设校园平面图是一个无向网.顶点和边均含有相关信息.
二、问题分析和任务定义
(1)设计地图:地图标点,并编写景点相关信息,便于旅客查询。
(2)设计存储:用无向图,将景点存放,并设计各个结点之间的拓扑图。
(3)设计算法:使用Dijkstra算法计算最短路径。
限制条件:景点数量>=10
三、逻辑设计
【存储结构】
(1)无向图存储路径信息(拓扑图见预设数据)
#define M 999
int Map[N][N] = {
{M,100,M,200,M,M,M,M,M,M},
{100,M,80,150,M,M,M,M,M,M},
{M,80,M,M,120,110,M,M,M,M},
{200,150,M,M,50,M,M,M,M,M},
{M,M,120,50,M,M,150,230,M},
{M,M,110,M,M,M,80,60,M,M},
{M,M,M,M,M,80,M,M,M,100},
{M,M,M,M,150,60,M,M,90,70},
{M,M,M,M,230,M,M,90,M,50},
{M,M,M,M,M,M,100,70,50,M}
};
(2)景点类ScenicSpot
ADT ScenicSpot{
Data
编号
景点名称
景点介绍
Operation
Constructor
初始数值:编号、名称、介绍
功能:初始化对象的上述内容
print
前置条件:无
输入:无
功能:表格状输出列表编号与名称
输出:列表编号与名称
后置条件:无
printIntroduce
前置条件:无
输入:无
功能:输出景点介绍
输出:景点介绍
后置条件:无
end ADT
(3)景点容器
vector<ScenicSpot> SpotList;
(4)路径数组
int path[N];
【查询景点信息模块】
void query():简单的询问信息功能
【查询景点路径模块】
void findPath():询问功能,返回最短路径功能,输出路径功能
int Dijkstra():Dijkstra最短路算法
【调用关系图】
【流程图】
四、物理设计
【景点类】
class ScenicSpot {
public:
ScenicSpot(int noble, string name, string introduce) { //构造函数
this->noble = noble;
this->name = name;
this->introduce = introduce;
}
void print() { //用于表格状输出列表总体信息
cout << noble << "\t" << name << "\n";
}
void printIntroduce() {//输出景点介绍
cout << introduce << endl;
}
private:
int noble;
string name;
string introduce;
};
【查询景点信息】
void query() {
cout << "你要查询的景点编号(1-10):";
int n;
cin >> n;
if (n < 1 || n>10) {
cout << "输入不合法!\n";
return;
}
SpotList[n - 1].print();
SpotList[n - 1].printIntroduce();
}
【查询景点路径】
void findPath() {
cout << "你要查询从哪个景点到哪个景点的最短路径:";
int a, b;
cin >> a >> b;
if (a >= 1 && a <= 10 && b >= 1 && b <= 10) {
cout << "最短路径长度为" << Dijkstra(a, b) << "\n";
stack<int> way;
int nowAt = b - 1;
while (nowAt != -1) {
way.push(nowAt + 1);
nowAt = path[nowAt];
}
cout << "最优路径为:";
while (!way.empty()) {
cout << way.top();
if (way.top() != b) cout << "->";
way.pop();
}
cout << '\n';
}
else {
cout << "输入不合法!\n";
}
}
【Dijkstra算法】
int Dijkstra(int A, int B) {
int a, b;
a = A - 1; b = B - 1;
bool visited[N]; //用来记录
int dist[N];
for (int i = 0; i < N; i++) {
dist[i] = Map[i][a]; //
visited[i] = false; //标记为未访问
if (i == a) path[i] = -1;
else if (Map[i][a] == M) path[i] = -1;
else path[i] = a;
}
dist[a] = 0; visited[a] = true; path[a] = -1; //初始化
int hasVisited = 1;
while (hasVisited < N) {
int min, minLen = M;
for (int i = 0; i < N; i++)
if ((!visited[i]) && (dist[i] < minLen)) {
min = i; minLen = dist[i];
}
visited[min] = true;
hasVisited++;
for (int i = 0; i < N; i++)
if (dist[i] > dist[min] + Map[min][i]) {
dist[i] = dist[min] + Map[min][i];
path[i] = min;
}
}
return dist[b];
}
五、预设数据
【景点地图】
景点编号 | 景点名称 |
---|---|
1 | 三号组团 |
2 | 西苑食堂 |
3 | 明德园 |
4 | 文体中心 |
5 | 西操场 |
6 | 文理大楼 |
7 | 北苑 |
8 | 求索园 |
9 | 东苑食堂 |
10 | 图书馆 |
【拓扑图】
注:数据不与现实吻合
项目3:算术表达式求解
一、设计主要要求
【问题描述】
设计一个简单的算术表达式计算器。
【基本要求】
实现标准整数类型的四则运算表达式的求值(包含括号,可多层嵌入).
【测试数据】
(30+2*70)/3-12*3
5+(9*(62-37)+15)*6
要求自行设计非法表达式,进行程序测试,以保证程序的稳定运行。
【实现提示】
可以设计以下辅助函数
status isNumber(char ReadInChar); //视ReadInchar 是否是数字而返回 TRUE或 FALSE 。
int TurnToInteger(char IntChar); // 将字符’0’.’9’ 转换为整数 9
二、问题分析和任务定义
可以实现合法算式的运算。
考虑优先级问题,()内优先,其次*/,最次+-。
采用符号栈存储运算符,用数据栈存储已运算的数据。
可以识别非法算是并给出提示。
注意点:谨慎考虑符号重复出现时的情况、处理结束符号问题、处理第一个元素加入栈时的判断。
三、逻辑设计
【数据结构】
(1)优先级矩阵
//优先级矩阵 1表示优先级更高,0表示优先级更低,2表示括号相遇,-1表示不合法
//0~5 表示 (+-*/)
int priority[6][6] = {
{0,0,0,0,0,2},
{0,1,1,0,0,1},
{0,1,1,0,0,1},
{0,1,1,1,1,1},
{0,1,1,1,1,1},
{-1,1,1,1,1,1}
};
(2)数据栈和符号栈
stack<double> values; //存放运算数
stack<char> operators; //存放运算符
【主要功能函数】
int turnToNum(char c):将符号转变成对应的数字编号,非法符号返回-1
int compare(char a, char b):比较符号a和符号b的优先级
bool isNumber(char c):判断是否为数字
double result(double a, char c, double b):产生运算结果
【函数调用关系】
【流程图】
四、物理设计
【核心算法段】
for (int i = 0; i < (int)strlen(expression); i++) {
char &c = expression[i]; //c为引用当前字符
if (isNumber(c)) { num = num * 10 + c - '0'; }
else if (c == ' ') continue;
else if (turnToNum(c) != -1) {
if (c != '(') {
int pre = i - 1;
while (expression[pre] == ' ') pre--;
if (expression[pre] != ')') {
values.push((double)num);
if(show)cout << "压入" << num << "\n";
num = 0;
}
}
if (c == '=') break;
if (operators.empty()) {
if (show) cout << "压入" << c << "\n";
operators.push(c);
}
else {
bool goOn = true; //标记是否要继续
double a, b, r; //a,b分别存放数字栈最上面的两个数字,c存放运算结果
while (!operators.empty() && goOn) {
char cTop = operators.top();
switch (compare(cTop, c)) {
case 1: //栈顶优先级比当前大,消耗栈顶操作符,并计算
operators.pop();
if (show) cout << "弹出" << cTop << "\n";
b = values.top(); values.pop();
if (show)cout << "弹出" << b << "\n";
a = values.top(); values.pop();
if (show)cout << "弹出" << a << "\n";
r = result(a, cTop, b);
values.push(r);
if (show)cout << "压入" << r << "\n";
break;
case 0: //栈顶优先级比当前小,将当前符号压入
operators.push(c);
if (show)cout << "压入" << c << "\n";
goOn = false;
break;
case 2: //特殊的,当括号相遇,仅做弹出括号操作
if (show)cout << "弹出" << cTop << "\n";
operators.pop();
goOn = false;
break;
}
}
if (operators.empty() && goOn) {
operators.push(c);
if (show)cout << "压入" << c << "\n";
}
}
}
else {
cout << "该算式非法\n";
continue;
}
}
while (!operators.empty()) {
int a, b, c;
char cTop = operators.top();
operators.pop();
b = values.top(); values.pop();
a = values.top(); values.pop();
c = result(a, cTop, b);
values.push(c);
}
cout << "Answer is " << values.top() << endl;
补充:这个算法很基础......也找不到什么特别能体现水平的点......优化了一下报错机制,处理了一下开头负数(如-1*3这样的算式)。如果有两个符号中间没有数字的情况,程序会认为中间的数字为0(如1+*2等价为1+0*2答案为1,而非报错),姑且算是机制而不是bug吧^ ^
补充2:本程序设计了开发者模式(输入&开启),打开后将显示栈中元素每一次的压入和弹出情况,便于观察内部运行的逻辑(笔者修改bug时就一直使用的是这个方法)。
开发工具
Windows端(笔者的台式机):Microsoft Visual Studio 2019
Mac端(笔者的笔记本):Microsoft Visual Studio Code
结语
感谢观看!