STL综合题:歌唱比赛

1. 比赛机制

某学校举行一场唱歌比赛,共有24个人参加,按参加顺序设置参赛号(参赛号为100至123)。
每个选手唱完一首歌之后,由10个评委分别打分。该选手的最终得分是去掉一个最高分和一个最低分,求得剩下的8个评分的平均分。

比赛共三轮,前两轮为淘汰赛,第三轮为决赛。选手的名次按得分降序排列,若得分一样,按参赛号升序排名。

  • 第一轮分为4个小组,根据参赛号顺序依次划分,比如100-105为一组,106-111为第二组,依次类推,每组6个人,每人分别按参赛号顺序演唱。当小组演唱完后,淘汰组内排名最后的三个选手,然后继续下一个小组的比赛。
  • 第二轮分为2个小组,每组6人,每个人分别按参赛号顺序演唱。当小组演唱完后,淘汰组内排名最后的三个选手,然后继续下一个小组的比赛。
  • 第三轮只剩下6个人,本轮为决赛,不淘汰选手,本轮目的是赛出每个人的名次。该6人按参赛号顺序分别演唱。

2. 要求

  • 请打印出所有选手的名字与参赛号,并以参赛号的升序排列。
  • 请打印出第1轮和第2轮淘汰赛中,各小组选手的名字与选手得分,并以名次的顺序排列。
  • 请打印出第1轮淘汰赛中被淘汰的歌手的名字(不要求打印顺序)。
  • 请打印出第2轮淘汰赛中被淘汰的歌手的分数,并以名次的降序排列。

3. 题目分析

3.1 总体分析所需要的结构体,类,类的外部接口,类的成员变量;

3.1.1 定义歌手结构体

//在文件Singer.h中定义歌手的结构体
struct Singer
{
   
	string  strName;		//名字
	int  iLatestScore;		//最新得分
};

3.1.2 定义歌唱比赛类

#include "qjhself.h"
#include "Singer.h"
using namespace std;

//定义歌唱比赛类
//类的声明在SingingCompetition.h中,类的实现在SingingCompetition.cpp中
//以下为对外开放的类成员方法(类的外部接口),我们主要要实现以下四个方法。
class CSingingCompetition
{
   

public://为了减少代码量,此处设为公有

	//总体分析所需要的结构体,类,类的外部接口,类的成员变量;

   //歌唱比赛类的成员变量

   //所有的参赛歌手的ID与歌手信息的映射集合。
	map<int, Singer>  m_mapSinger; //int:参赛ID,Singer:参加比赛的歌手信息。

	//剩余歌手(没被淘汰的歌手)的参赛ID的集合。
	list<int> m_lstRemainingID;	//int:剩余歌手的参赛ID。

	//当前演唱小组的歌手分数与歌手参赛ID的映射集合。
	multimap<int, int, greater<int> >  m_mltmapCurGroup;
	//第一个int: 歌手分数
	//第二个int: 歌手参赛ID
	//greater<int>: 函数对象,用于歌手分数的降序排列

	//第一轮淘汰赛中被淘汰的歌手名字的集合。
	vector<int> m_vecIDBeEliminatedInFirstRound;//int: 歌手的参赛ID

	//第二轮淘汰赛中被淘汰的歌手分数的集合。
	multiset<int> m_mltsetScoreBeEliminatedInSecondRound;//int: 歌手的分数

	//第几轮比赛
	int m_iRound;//值为1,第一轮;值为2,第二轮;值为3,第三轮

public:
	//报名参加比赛
	void JoinCompetition();

	//第一轮淘汰赛
	void FirstKnockout();

	//第二轮淘汰赛
	void SecondKnockout();

	//决赛
	void Finals();

	//淘汰赛规则
	void Knockout();

	//生成歌手的分数
	void MakeScore(Singer& singer);

	//小组演唱完毕,打印小组得分情况
	void PrintGroupScore();

	//在当前小组中淘汰歌手
	void EraseInCurGroup();

	//在剩余歌手中删除歌手
	void EraseInRemainingID(list<int>::iterator it);

	// 歌唱比赛类初始化
	CSingingCompetition();

};

3.1.3 歌唱比赛类的成员变量

//所有的参赛ID与歌手的映射集合。
map<int, Singer>  m_mapSinger;//int:参赛ID,Singer:参加比赛的歌手。

//剩余歌手(没被淘汰的歌手)的参赛ID的集合。
list<int> m_lstRemainingID;	//int:剩余歌手的参赛ID。

//当前演唱小组的歌手分数与歌手参赛ID的映射集合。
multimap<int, int, greater<int> >  m_mltmapCurGroup;
//第一个int: 歌手分数
//第二个int: 歌手参赛ID
//greater<int>: 函数对象,用于歌手分数的降序排列

//第一轮淘汰赛中被淘汰的歌手名字的集合。
vector<int> m_vecIDBeEliminatedInFirstRound;//int: 歌手的参赛ID

//第二轮淘汰赛中被淘汰的歌手分数的集合。
multiset<int> m_mltsetScoreBeEliminatedInSecondRound;//int: 歌手的分数
		
//第几轮比赛
int m_iRound;//值为1,第一轮;值为2,第二轮;值为3,第三轮
//所有的参赛ID与歌手的映射集合。
map<int, Singer>  m_mapSinger;//int:参赛ID,Singer:参加比赛的歌手。

为什么选择map ? 而不选择其他容器呢?
答:每个歌手只想在内存中保存一份对象实例。另外我们希望能很快通过参赛ID来找到歌手和他的相关信息。从这里可以看出,我们选择关联性的容器是比较合适的。所以序列性的容器(Vector、List、Deque)都被排除了。关联性的容器有Set和Map两种。而Set是没有映射功能,而我们这边需要有映射关系的容器,所以只有Map类的容器才可以。而MultiMap的键值是可以重复的,但我们这边的ID是不能重复的,所以最后我们选择map容器来作为存放歌手的容器。

//剩余歌手(没被淘汰的歌手)的参赛ID的集合。
list<int> m_lstRemainingID;	//int:剩余歌手的参赛ID。

为什么要选用list,其它容器代替合适吗?
答:这个容器,是需要频繁地从容器的不确定位置删除元素的。List内部的 每个节点都是通过指针值的指向位置来相连接,在list中删除元素或插入元素,不会浪费或移动存储空间,只需要修改相应元素的指针指向值。vector或deque在中间或头部删除元素,将移动大量的空间,set与map是属于关联性容器,本身是排序的,而排序在这里是多余的,它们的内部实现比list复杂,删除的效率也没有list的删除效率高。

//当前演唱小组的歌手分数与歌手参赛ID的映射集合。
multimap<int, int, greater<int> >  m_mltmapCurGroup;
//第一个int: 歌手分数
//第二个int: 歌手参赛ID
//greater<int>: 函数对象,用于歌手分数的降序排列

为什么要采用multimap,选用其它容器合适吗?
答:我们要记录小组成员得分情况,而得分情况需要降序排序,我们就需要选一个容器,可以降序排列分数。这时我们可以选择set,multiset,map,multimap这些关联性容器,我们又需要从排序后的分数映射出分数的主人,也就是参赛ID,所以,我们排除了set,multiset,而歌手的分数是可能一样的,所以,排除了map,而选择了multimap。

//第一轮淘汰赛中被淘汰的歌手名字的集合。
vector<int> m_vecIDBeEliminatedInFirstRound;//int: 歌手的参赛ID

为什么选择vector,其它容器合适吗?
答:按题目的要求,这边的容器只要存储数据就行,没有删除元素的操作,没有排序的操作,选vector刚刚够用,且vector支持随机存储是最好的,数据也在内存中也是连续的。deque的第一个元素的位置不确定,随机存储性没vector好,而且deque的push_front与pop_back也是多余的,我们选择容器,够用就好。list不支持随机存储,要找到元素的下一个元素,时间久,且它提供的插入删除操作在这这是多余的。set的排序是多余的,不支持随机存储。map的排序也是多余的,映射也是多余的。

//第二轮淘汰赛中被淘汰的歌手分数的集合。
multiset<int> m_mltsetScoreBeEliminatedInSecondRound;//int: 歌手的分数

为什么选用multiset?其它容器合适吗?
答:由于这里需要排序,又不需要映射,所以,可以选set与multiset,不过分数是可能重复的,所以,选用multiset。

//第几轮比赛
int m_iRound;//值为1,第一轮;值为2,第二轮;值为3,第三轮

3.1.4 歌唱比赛类初始化

CSingingCompetition::CSingingCompetition
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值