这个案例适合刚刚看完STL的知识点或者刚刚入门STL的小伙伴们。我也是在前段时间刚刚学完了STL的知识,没学之前觉得STL多么的高深莫测,学完以后感觉也就那样,就是一帮大牛封装了一个模板库,让我们按照他们指定的规则去调用就可以了,学习的过程中你每时每刻都在接触你之前所学过的函数模板和类模板。
案例:学校演讲比赛
1)某市举行一场演讲比赛,共有24个人参加,按参加顺序设置参赛号。比赛共三轮,前两轮为淘汰赛,第三轮为决赛。
2)比赛方式:分组比赛
第一轮分为4个小组,根据参赛号顺序依次划分,比如100-105为一组,106-111为第二组,依次类推,每组6个人,每人分别按参赛号顺序演讲。当小组演讲完后,淘汰组内排名最后的三个选手,然后继续下一个小组的比赛。
第二轮分为2个小组,每组6人,每个人分别按参赛号顺序演讲。当小组完后,淘汰组内排名最后的三个选手,然后继续下一个小组的比赛。
第三轮只剩下6个人,本轮为决赛,选出前三名。
3)选手每次要随机分组,进行比赛。
4)比赛评分:10个评委打分,去除最低、最高分,求平均分
每个选手演讲完由10个评委分别打分。该选手的最终得分是去掉一个最高分和一个最低分,求得剩下的8个成绩的平均分。选手的名次按得分降序排列,若得分一样,按参赛号升序排名。
上代码
#include<iostream>
#include<iterator>
using namespace std;
#include<string>
#include<vector>
#include<list>
#include<set>
#include<deque>
#include<map>
#include<algorithm>
#include<functional>
#include<numeric>
#include<iterator>
class Speaker //包含选手的姓名和得分
{
public:
string m_name;
int m_score[3];
};
//产生选手
int GenSpeaker(map<int,Speaker> &mapSpeaker,vector<int> &v)
{
string str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
random_shuffle(str.begin(),str.end());
for(int i=0;i<24;i++)
{
Speaker temp;
string s = "选手";
temp.m_name = s+str[i];
mapSpeaker.insert(pair<int,Speaker>(100+i,temp));
}
for(int i=0;i<24;i++)
{
v.push_back(100+i);//参加比赛的人员
}
return 0;
}
//产生抽签
int speech_contest_draw(vector<int> &v)
{
random_shuffle(v.begin(),v.end());
return 0;
}
//选手比赛
int speech_contest(int index,vector<int> &v1,map<int,Speaker> &mapSpeaker,vector<int> &v2)
{
multimap<int,int,greater<int>> multmapGroup;
int count = 0;
for(vector<int>::iterator it=v1.begin();it!=v1.end();it++)
{
count++;
//打分
{
deque<int> dscore;
for(int j=0;j<10;j++)//十个评委打分
{
int score = 50+rand()%50;//用了一个随机数种子
dscore.push_back(score);
}
sort(dscore.begin(),dscore.end());
dscore.pop_back();
dscore.pop_front();//删除最高 最低分
//求平均分
int scoresum = accumulate(dscore.begin(),dscore.end(),0);
int scoreavg= scoresum/dscore.size();
mapSpeaker[*it].m_score[index] = scoreavg;//选手得分 存入容器中
multmapGroup.insert(pair<int,int>(scoreavg,*it));
}
if(count % 6 == 0)//按六个参赛成员分为一个小组并打印成绩
{
cout<<"小组的比赛成绩"<<endl;
for(multimap<int,int,greater<int>>::iterator it=multmapGroup.begin();it!=multmapGroup.end();++it)
{
//编号 姓名 得分
cout<<"#"<<it->second<<"\t"<<mapSpeaker[it->second].m_name<<"\t"<<it->first<<endl;
}
//把小组前三名放到下一轮参赛的vector容器中
while(multmapGroup.size()>3)
{
multimap<int,int,greater<int>>::iterator it = multmapGroup.begin();
v2.push_back(it->second);
multmapGroup.erase(it);
}
multmapGroup.clear();//清除本小组成绩
}
}
return 0;
}
//打印每一轮晋级名单
int speech_contest_print(int index,vector<int> &v,map<int,Speaker> &mapSpeaker)
{
printf("第%d轮 晋级名单\n",index+1);
for(vector<int>::iterator it = v.begin();it!=v.end();++it)
{
cout<<"#"<<*it<<"\t"<<mapSpeaker[*it].m_name<<"\t"<<mapSpeaker[*it].m_score[index]<<endl;
}
return 0;
}
void main()
{
//容器的设计
map<int,Speaker> mapSpeaker; // 参加选手的id,参加比赛的选手
vector<int> v1;//第一轮演讲比赛名单
vector<int> v2;//第二轮演讲比赛名单
vector<int> v3;//第三轮演讲比赛名单
vector<int> v4;//最后前三名 演讲比赛名单
//产生选手 得到第一轮选手的比赛名单
GenSpeaker(mapSpeaker,v1);
//第一轮 选手抽签 选手比赛 查看比赛结果
cout<<"按确认键开始第1轮比赛"<<endl;
cin.get();
speech_contest_draw(v1);
speech_contest(0,v1,mapSpeaker,v2);
speech_contest_print(0,v2,mapSpeaker);
//第二轮 选手抽签 选手比赛 查看比赛结果
cout<<"\n\n\n确认键开始第2轮比赛"<<endl;
cin.get();
speech_contest_draw(v2);
speech_contest(1,v2,mapSpeaker,v3);
speech_contest_print(1,v3,mapSpeaker);
//第三轮 选手抽签 选手比赛 查看比赛结果
cout<<"\n\n\n确认键开始第3轮比赛"<<endl;
cin.get();
speech_contest_draw(v3);
speech_contest(2,v3,mapSpeaker,v4);
speech_contest_print(2,v4,mapSpeaker);
system("pause");
}
1.毫无疑问,我们首先要做一个演讲者Speaker的类(其实也可以用结构体来定义),用来存储我们的参赛者的姓名和比赛的分数,可以看到比赛时分三轮的,所以存储分数的是一个数组,而不是单个的数值,每一轮比赛过后通过比赛的轮次数作为索引,存入到定义的m_score[3]的数组中。
2.在考虑,需要选用的容器类型,想找一个既可以存储参赛者的个人信息(即姓名和得分),又可以存储参赛者的编号的容器,那很容易就可以想到map让其,因为是map里面放的是键(key)值(value)对嘛,键(key)对应编号,值(value)对应Speaker所定义的对象嘛,就是前面说的参赛者的个人信息。还有,我们每一轮的参赛成员也需要用容器装起来,这个很容易就能想到vector容器嘛,再想想,总共三轮比赛,那就需要三个vector容器,最后还需要一个装前三名的容器嘛,这也就是程序中定义四个vector容器的原因。在还有,题目中说了(选手的名次按得分降序排列,若得分一样,按参赛号升序排名。),第70行的代码中map是以得分为键(key)的,所以要是定义了普通的map,是不允许出现相同的键的,所以这也就是为什么要定义multimap。还有还有,评委打分以后算平均分的过程中,我们需要去掉最高分去掉最低分,这种操作就交给我们的队列deque容器了因为它可以自动排序并且可以很轻松的对头部和尾部进行删除操作。
3.我再来说几个我觉得是难点的地方,第一,要弄清楚map容器和multimap容器中的键和for循环中迭代器iterator所指的具体的对象,先看75-79行这个循环,multimap<int,int,greater<int>>::iterator it=multmapGroup.begin();这个迭代器(就是所谓的指针嘛)指向的对象是multmapGroup中的元素,,再看下面的cout那行语句,it->second这就是指的是第70行multmapGroup.insert(pair<int,int>(scoreavg,*it))中的第二个*it,我们往上追溯可以看到这是v1中的迭代器,v1的迭代器解引用就是选手的编号,所以这里输出选手的编号,接在后面的it->first就是代表的访问得分并打印了嘛;再看mapSpeaker[it->second].m_name中的mapSpeaker[it->second],就是通过选手的编号来寻找编号对应的参赛选手对象,在对m_name来进行访问,在将其打印出来。最后定义的speech_contest_print函数中也有类似的cout语句,你也可以按照我上面说的方法来仔细推导一番。
4.以上我们说到了容器,迭代器,下面来说说算法和仿函数(函数对象),第43行random_shuffle(v.begin(),v.end())和第63行sort(dscore.begin(),dscore.end());这就是排序算法,还有第67行的accumulate(dscore.begin(),dscore.end(),0);的求和算法,这些算法都是STL中封装好的函数,只需要正确调用就就可以了,还要学会追踪这些算法,看看它的参数列表的参数和返回值,这对学习STL是非常重要的。这个例子中我们也用到了预定了的函数对象,第49行的multimap<int,int,greater<int>> multmapGroup;中greater<int>就是预定义好的仿函数,代表按照键的大小(这里指的是选手比赛得分)从大到小的排列。这种是预定义的函数对象,我们也可以自己写函数对象。