前言
最近在学C++,看到一个关于使用STL的小作业,一时兴起想做一做。
作业题目
某地举办一次比赛,共有24人参加,比赛总共3轮,前两轮为淘汰赛,最后一轮为决赛。
比赛方式:分组比赛,每组6人;选手每次要随机分组,进行比赛。例如:
- 第一轮分为4个组,每组6个人,比如所有人的编号为100-123,对这些人编号后随机分为4组,比赛结束后淘汰组内最后3个人,然后继续下一轮比赛。
- 第二轮分为2个组,每组6个人,比赛结束后淘汰组内最后3个人,然后进行决赛。
- 决赛只剩下6个人,选出前三名,获奖。
评分规则:10个评委打分,每轮比赛都是去除最高分和最低分,对剩下8个成绩求平均分,选手的名次按平均分降序排序。
要求:用STL编程,每轮比赛结束后打印出晋级的名单。
开始
注:全部在一个.cpp文件里实现,不分文件编写了。
实现思路:根据下面的代码块来讲解。
- 先导入头文件
#include <iostream>
#define _CRT_SECURE_NO_DEPRECATE
using namespace std;
#include <vector>
#include <deque>
#include <algorithm>
#include <numeric>
- 定义Person类
class Person
{
public:
string Name;
deque<int> Score;
int Id;
double MeanScore;
};
比赛成绩用一个队列 deque 来存,好处是:排序后便于去掉最高分和最低分,使用pop_front()和pop_back()即可。
- 产生选手
void CreatPerson(vector<Person>& v)
{
string names = "ABCDEFGHIJKLNMOPQRSTUVWX";
int id = 100;
for (int i = 0; i < names.size(); ++i)
{
Person p;
p.Name = "选手";
p.Name += names[i];
p.Id = id + i;
v.push_back(p);
}
}
用24个字母代替24个选手,函数传入的是一个vector的引用,里面放的是所有选手的对象实例。
- 给选手打分
void getScore(vector<Person>& v)
{
for (vector<Person>::iterator begin = v.begin(); begin != v.end(); ++begin)
{
for (int i = 0; i < 10; ++i)
{
(*begin).Score.push_back(rand() % 41 + 60);
}
}
}
随机生成60-100之间的数字作为每个选手的分数,用一个迭代器遍历,把分数加到队列Score里。
- 计算所有选手平均分,并排序
class AvgCompare
{
public:
bool operator()(Person& p1, Person& p2)
{
return p1.MeanScore > p2.MeanScore;
}
};
bool AvgCompare1(Person& p1, Person& p2)
{
return p1.MeanScore > p2.MeanScore;
}
void calAvgScore(vector<vector<Person>>& vGroup)
{
for (vector<vector<Person>>::iterator begin1 = vGroup.begin(); begin1 != vGroup.end(); ++begin1)
{
// 对每一组的选手,计算平均分
for (vector<Person>::iterator begin2 = (*begin1).begin(); begin2 != (*begin1).end(); ++begin2)
{
// 先把分数排序
sort((*begin2).Score.begin(), (*begin2).Score.end(), greater<int>());
// 去掉最高、最低分
(*begin2).Score.pop_front();
(*begin2).Score.pop_back();
// 计算平均分
double mean = double(accumulate((*begin2).Score.begin(), (*begin2).Score.end(), 0)) / double((*begin2).Score.size());
(*begin2).MeanScore = mean;
// 下面这句按照平均分对选手排序的写法是错误的!
//sort((*begin2).Score.begin(), (*begin2).Score.end(), AvgCompare()); 错误!!!
}
// 按照平均分对选手排序
//sort((*begin1).begin(), (*begin1).end(), AvgCompare());
//sort((*begin1).begin(), (*begin1).end(), AvgCompare1);
sort((*begin1).begin(), (*begin1).end(), [](Person& p1, Person& p2) {return p1.MeanScore > p2.MeanScore; });
}
}
定义一个AvgCompare类,在使用sort()方法对自定义数据类型排序的时候,当仿函数使用。
函数体传入的参数是包含4个小vector的大vector,第一个for遍历每个小vector,第二个for遍历每个小vector里的每个选手。
第2个for里,计算平均分的时候,由于传入的vector里的成绩是未排序的,因此需要先排序,这里使用了内建函数 greater<int>();
第2个for跳出后,再按平均分对选手排序,有三种写法,第一种是用仿函数,第二种忘了叫什么了(额),第三种用了匿名函数(写起来更清爽)。
注意,第1个for里最后注释的那句是错的,两点错误,首先,需求是根据MeanScore来对Person排序,而这句是根据MeanScore对Score排序(完全乱套),其次,参数前后类型要一致,*begin1是vector<Person>,从而才能对其使用sort()算法,(*begin2)是Person,不是容器,当然无法使用sort()。
- 第k轮比赛,对所有选手随机分组,进行比赛,获得分数
// 参数含义:第round轮比赛,共n组,每组k人
vector<vector<Person>> Round(vector<Person> &v, int round, int n, int k)
{
cout << endl << "+++++++++++++++++++++++++" << endl;
if (round == 3)
{
cout << "--------决赛" <<"--------";
}
else
{
cout << "--------Round " << round << "--------";
}
cout << endl << "+++++++++++++++++++++++++" << endl;
vector<vector<Person>> vGroup;
// 随机打乱所有选手
random_shuffle(v.begin(), v.end());
// 创建保存4组选手的容器,并添加进去
for (int i = 0; i < n; ++i)
{
vector<Person>vTemp;
vGroup.push_back(vTemp);
}
int num = 0;
for (vector<vector<Person>>::iterator iter = vGroup.begin(); iter != vGroup.end(); ++iter)
{
// 给每位选手分组,并打分
for (int i = num; i < v.size(); ++i)
{
// 先判断Score容器是否有分数,如果有,需要清空,再打分,不然新的一轮比赛会把分数加在后面 -- 用clear,清空元素,但不回收空间
if (!v[i].Score.empty())
{
v[i].Score.clear();
}
v[i].MeanScore = 0;
(*iter).push_back(v[i]);
++num;
if (num % k == 0)
{
break;
}
}
getScore(*iter);
}
return vGroup;
}
传入的参数是包含了所有选手的vector,为了后面进行打乱。
随机分组的思路是:
1. 先把24个人全部放到一个vector里,然后用random_shuffle打乱;
2. 定义4个空的小vector,把24个人分别放到4个vector中;
3. 利用getScore()函数给选手随机生成分数,注意需要先判断队列Score是否为空,如果有分数在里面,需要先清空,不然上一轮比赛的分数会存在里面。
- 淘汰最后3个选手,再把所有选手放在一起
vector<Person> getoutLast3(vector<vector<Person>>& vGroup)
{
vector<Person>vGroupNew;
for (vector<vector<Person>>::iterator begin = vGroup.begin(); begin != vGroup.end(); ++begin)
{
// 对每一组的选手,淘汰最后三人
for (int i = 0; i < 3; ++i)
{
(*begin).pop_back();
}
for (vector<Person>::iterator begin1 = (*begin).begin(); begin1 != (*begin).end(); ++begin1)
{
vGroupNew.push_back(*begin1);
}
}
return vGroupNew;
}
传入的vGroup是排好序的,因此只需要pop_back最后三个人即可;用一个vGroupNew来存放所有晋级的选手。
- 输出测试
void out1(vector<Person>& v)
{
for (vector<Person>::iterator begin = v.begin(); begin != v.end(); ++begin)
{
cout << "name:" << (*begin).Name << " " << "id:" << (*begin).Id << " " << "score:";
for_each((*begin).Score.begin(), (*begin).Score.end(), [](int val) {cout << val << " "; });
cout << "mean score:" << (*begin).MeanScore << endl;
}
}
void out2(vector<vector<Person>>& vGroup)
{
int num = 1;
for (vector<vector<Person>>::iterator begin1 = vGroup.begin(); begin1 != vGroup.end(); ++begin1) // begin1指向每个vector,共有4个
{
cout << "------------------------------------------------------------------------" << endl;
cout << "group" << num << endl;
cout << "------------------------------------------------------------------------" << endl;
for (vector<Person>::iterator begin2 = (*begin1).begin(); begin2 != (*begin1).end(); ++begin2)
{
cout << "name:" << (*begin2).Name << " " << "id:" << (*begin2).Id << " " << "score:";
for_each((*begin2).Score.begin(), (*begin2).Score.end(), [](int val) {cout << val << " "; });
cout << " " << "mean score:" << (*begin2).MeanScore << endl;
}
++num;
}
}
out1打印选手的信息,是针对只有一个vector的情况;out2是针对大vector里有4个vector的情况。
- 主函数
void test()
{
// 创建存储Person的容器
vector<Person> v0;
// 产生选手
CreatPerson(v0);
//out1(v0);
/***************第一轮比赛***************/
// 对所有选手随机分组,并进行比赛,获得分数
vector<vector<Person>>vGroup1 = Round(v0, 1, 4, 6);
//out2(vGroup1);
// 计算每一组选手的平均分
calAvgScore(vGroup1);
cout << endl;
//out2(vGroup1);
//淘汰每组最后3名选手,并重新组队
vector<Person>v1 = getoutLast3(vGroup1);
out1(v1);
/***************第二轮比赛***************/
vector<vector<Person>>vGroup2 = Round(v1, 2, 2, 6);
calAvgScore(vGroup2);
//out2(vGroup2);
vector<Person>v2 = getoutLast3(vGroup2);
out1(v2);
/***************决赛***************/
vector<vector<Person>>vGroup3 = Round(v2, 3, 1, 6);
calAvgScore(vGroup3);
//out2(vGroup3);
vector<Person>v3 = getoutLast3(vGroup3);
cout << "最终获奖:" << endl;
out1(v3);
}
int main()
{
test();
return 0;
}
- 输出
+++++++++++++++++++++++++
--------Round 1--------
+++++++++++++++++++++++++
name:选手C id:102 score:99 91 89 86 84 78 76 69 mean score:84
name:选手N id:112 score:97 97 95 90 80 71 70 65 mean score:83.125
name:选手L id:111 score:96 91 83 82 77 76 76 65 mean score:80.75
name:选手F id:105 score:95 89 88 87 86 81 75 68 mean score:83.625
name:选手H id:107 score:92 92 88 87 86 79 71 68 mean score:82.875
name:选手E id:104 score:91 88 81 79 75 70 68 64 mean score:77
name:选手M id:113 score:97 93 91 86 85 77 75 73 mean score:84.625
name:选手Q id:116 score:96 94 92 89 79 72 67 64 mean score:81.625
name:选手O id:114 score:91 86 84 81 78 75 72 70 mean score:79.625
name:选手I id:108 score:98 94 92 92 89 87 86 77 mean score:89.375
name:选手R id:117 score:98 94 89 86 85 84 79 78 mean score:86.625
name:选手U id:120 score:93 87 86 82 80 78 64 62 mean score:79
+++++++++++++++++++++++++
--------Round 2--------
+++++++++++++++++++++++++
name:选手U id:120 score:97 93 92 90 89 87 77 71 mean score:87
name:选手Q id:116 score:89 86 83 82 80 80 78 70 mean score:81
name:选手C id:102 score:95 93 88 83 80 78 67 63 mean score:80.875
name:选手F id:105 score:95 93 91 91 90 86 76 71 mean score:86.625
name:选手R id:117 score:95 91 90 88 85 80 70 68 mean score:83.375
name:选手L id:111 score:94 92 88 87 79 78 77 67 mean score:82.75
+++++++++++++++++++++++++
--------决赛--------
+++++++++++++++++++++++++
最终获奖:
name:选手F id:105 score:97 90 88 87 86 79 79 63 mean score:83.625
name:选手C id:102 score:94 91 90 88 77 72 64 61 mean score:79.625
name:选手U id:120 score:94 88 86 81 77 76 68 65 mean score:79.375
后记
尽管代码有点长,但思路其实很简单,主要是想练习一下STL最基础的使用。