-
multimap
- 头文件:#include <map>
multimap容器里的元素,都是pair形式的
multimap<T1, T2> mp;
则mp里的元素都是如下类型:
struct{ T1 first; //关键字 T2 second; //值 };
multimap中的元素按照first排序,并可以按first进行查找
缺省的排序规则是 “a.first < b.first” 为 true,则 a 排在 b 前面
#include <iostream>
#include <map> //使用multimap和map需要包含此头文件
#include <cstring>
using namespace std;
struct StudentInfo //结构体,存储学生信息
{
int id; //学生学号
char name[20]; //学生姓名
};
struct Student
{
int score; //关键字(first),表示排序的参考是学生成绩
StudentInfo info; //值(second),这是一个结构体
};
typedef multimap<int,StudentInfo> MAP_STD;
//typedef(type define),此后MAP_STD等价于multimap<int,StudentInfo>
//typedef int * PINT; 则此后PINT等价于int *。即PINT p; 等价于 int * p;
int main()
{
MAP_STD mp; //即multimap<int,StudentInfo> mp;
Student st; //结构体类型
char cmd[20];
while( cin >> cmd ) //接收输入的一行字符到cmd[]
{
if( cmd[0] == 'A' ) //第一种情况,插入到容器multimap中
{
cin >> st.info.name >> st.info.id >> st.score; //接收输入的信息到合适的变量
mp.insert( make_pair( st.score, st.info ) ); //插入复制体到容器中
//make_pair生成一个pair<int, StudentInfo>变量
//其first等于st.score,second等于st.info
//上述insert完成后,mp中的两个变量的名称就确定了是st.score和st.info
}
else if( cmd[0] == 'Q' ) //第二种情况,查询
{
int score; //输入score查询已有记录中分数比score低的最高分获得者的信息
cin >> score;
MAP_STD::iterator p = mp.lower_bound (score);
//下界函数,查找大于等于score的最小下标,返回迭代器p
if( p!=mp.begin() ) //说明存在小于score的分数,指向的是score或者大于score
{
--p; //迭代器指向小于score的分数,最接近的那个
score = p->first; //p是multimap<int,StudentInfo>的迭代器,first是score
MAP_STD::iterator maxp = p; //新建一个迭代器,保存p的指向到maxp
int maxId = p->second.id; //p的second是info,有name和id两个变量
for( ; p!=mp.begin() && p->first == score; --p )
{
if( p->second.id > maxId ) //分数相同的有多个,则输出学号最大的
{
maxp = p;
maxId = p->second.id;
}
}
//如果上述循环是因为p==mp.begin()结束(而不是找到了分数不相等的p),即p--直接指向了begin,
//但是这里没有判断学号id的大小直接跳出,因为按分数排序,分数相同的人的学号的排序顺序是随机的
//则p指向的元素(mp.begin())还要处理,加一步判断学号id大小,确保找到分数相同的最大学号
if( p->first == score )
{
if( p->second.id > maxId )
{
maxp = p;
maxId = p->second.id;
}
}
cout << maxp->second.name << " "
<< maxp->second.id << " "
<< maxp->first << endl;
}
else //迭代器p==mp.begin(),说明没有人比score分数低
cout << "Nobody" << endl;
}
}
return 0;
}
- 关于lower_bound的使用:
困惑了我很久为什么用下界函数就可以做到找小于score的最大分数,以及循环判断条件为什么是st.begin?MAP_STD::iterator p = mp.lower_bound (score); //下界函数,查找大于等于score的最小下标,返回迭代器p
举例子说明:
- 由下表可知,当集合中没有小于输入的score的分数时,lower_bound(score)的返回值是指向大于等于score的最小值;又因为集合中按分数从小到大排列,所以这个最小值就是当前集合的begin()
- 除此之外的情况,要么指向score,要么指向end()
集合 {80,90,100,101} lower_bound( 100 ) 返回值->大于等于100的最小,即指向100 集合 {80,90,91} lower_bound( 100 ) 返回值->大于等于100的最小,没有,指向end() 集合 {101,102,103} lower_bound( 100 ) 返回值->大于等于100的最小,指向101,即begin()
-
map
- 头文件:#include <map>
- map和multimap的区别(与set和multiset的区别类似)
- map不能有关键字重复的元素(first重复)
- map可以使用[ ],下标为关键字,返回值为first和关键字相同的元素的second
- 插入元素可能失败(原先map容器里有该元素,因不能有重复,所以插入失败)
#include <iostream>
#include <map> //map容器头文件
#include <string>
using namespace std;
struct Student //保存学生信息的结构体
{
string name;
int score;
};
Student students[5] = {{"Jack",89},{"Tom",74},{"Cindy",87},{"Alysa",87},{"Micheal",98}};
typedef map<string, int> MP; //定义之后MP的类型为map<string, int>
//可知MP中元素都以string name进行排序,从小到大(字典序)
int main()
{
MP mp; //定义mp,类型是map<string, int>
for( int i=0; i<5; ++i )
mp.insert( make_pair(students[i].name,students[i].score) );
//make_pair生成一个pair<students[i].name, students[i].score>变量
//其first等于students[i].name,second等于students[i].score
//上述insert完成后,mp中的两个变量的名称就确定了是students[i].name和students[i].score
cout << mp["Jack"] << endl; //mp可以用[],下标是关键字students[i].name,返回值score=89
mp["Jack"] = 60; //将关键字Jack对应的值score修改为60
//从MP容器头开始遍历,因为容器内按关键字name排序,所以输出按名字的第一个字母的字典序排序
for(MP::iterator i = mp.begin(); i!=mp.end(); ++i)
cout << "(" << i->first << "," << i->second << ")" ;
cout << endl;
Student st; //定义一个结构体变量st
st.name = "Jack";
st.score = 99;
pair<MP::iterator, bool> p = mp.insert(make_pair(st.name,st.score));
//make_pair生成一个pair<st.name,st.score>变量,其first是st.name,second是st.score
//返回值是p,p是一个迭代器
if( p.second ) //返回值不为0,插入成功,p.first指向了刚刚被插入到MP中的元素
cout << "(" << p.first->first << "," << p.first->second << ") inserted" << endl;
else
cout << "insertion failed" << endl;
mp["Harry"] = 78;
//mp中不存在first为Harry的pair,所以执行mp["Harry"]的时候,自动将一个元素插入到mp中
//这个元素的first是"Harry",second是0
//然后执行 = 78 ,即将关键字为Harry的second修改为78
MP::iterator q = mp.find("Harry"); //find操作找到Harry,返回一个迭代器指向被找到的元素
cout << "(" << q->first << "," << q->second << ")" << endl;
return 0;
}
-
例题:【 map + set 】
分析:
- 输入:输入的单词是一行一行的,所以用cin读入string的就是一个单词。用结构体来保存单词和其对应的出现次数
- 设置一个map容器(不允许关键字单词string重复),关键字first设置成单词string,值second设置为该单词的出现次数。这样设计的理由是map可以用[ ]来操作,[关键字],关键字设置为单词string。每次读入一个单词string,都对map[string]进行加一操作,那么就会自动在对应单词的值second次数上进行加一
struct Word { int times; string wd; }
- 排序规则:先按单词的出现次数从多到少排序,出现次数相同的按单词第一个字母的字典序排序
struct Rule { bool operator() (const Word & w1, const Word & w2) const { if( w1.times != w2.times ) return w1.times > w2.times; //先按word出现的次数从高到低排列 else return w1.wd < w2.wd; //出现次数相等则按wd字典序排列 } }
- 用map来读入单词记录次数,用set来进行排序(set自动去重)。所以需要一个个从map容器里取出来放进set容器中,这样set容器就能自动根据上述定义的排序规则进行排序。之后再进行输出
完整代码如下:
#include <iostream> #include <set> //set容器头文件 #include <map> //map容器头文件 #include <string> using namespace std; struct Word { int times; string wd; }; struct Rule //set容器排序规则自定义 { bool operator() (const Word & w1, const Word & w2) const { if( w1.times != w2.times ) //出现次数不相等时从高到低排序 return w1.times > w2.times; else //出现次数相等时按单词字典序排序 return w1.wd < w2.wd; } }; int main() { string s; set<Word, Rule> st; //set容器,第一个参数是类型,第二个参数是自定义规则 map<string, int> mp; //map容器,第一个参数关键字类型是字符串型存单词,第二个是出现次数 while( cin >> s) //先将输入的单词保存在map容器中,并且记录出现次数 mp[s] ++ ; //如果单词本身不在map里,则先创建一个关键词为s的元素,初始值为0 //将map容器中元素复制到set容器中自动排序,迭代器类型为map,从头开始遍历 for( map<string, int>::iterator i = mp.begin(); i != mp.end(); ++i ) { Word tmp; //因为set容器中元素类型为struct Word{int times; string wd;} tmp.times = i->second; //将对应map容器中的int型数据second赋给times tmp.wd = i->first; //将对应map容器中的string型数据first赋给wd st.insert(tmp); //对应关系搭建完成后,将一个个Word型的tmp依次插入到set容器中 } //插入完成后,set容器中的数据就是有序的,直接输出 for( set<Word, Rule>::iterator i = st.begin(); i != st.end(); ++i ) cout << i->wd << " " << i->times << endl; return 0; }
-
热血格斗场
描述
为了迎接08年的奥运会,让大家更加了解各种格斗运动,facer新开了一家热血格斗场。格斗场实行会员制,但是新来的会员不需要交入会费,而只要同一名老会员打一场表演赛,证明自己的实力。
我们假设格斗的实力可以用一个正整数表示,成为实力值。另外,每个人都有一个唯一的id,也是一个正整数。为了使得比赛更好看,每一个新队员都会选择与他实力最为接近的人比赛,即比赛双方的实力值之差的绝对值越小越好,如果有两个人的实力值与他差别相同,则他会选择比他弱的那个(显然,虐人必被虐好)。
不幸的是,Facer一不小心把比赛记录弄丢了,但是他还保留着会员的注册记录。现在请你帮facer恢复比赛纪录,按照时间顺序依次输出每场比赛双方的id。
输入
第一行一个数n(0 < n <=100000),表示格斗场新来的会员数(不包括facer)。以后n行每一行两个数,按照入会的时间给出会员的id和实力值。一开始,facer就算是会员,id为1,实力值1000000000。输入保证两人的实力值不同。
输出
N行,每行两个数,为每场比赛双方的id,新手的id写在前面。
样例输入
3 2 1 3 3 4 2样例输出
2 1 3 2 4 2
分析:
- 会员id和其对应实力值可以放在一个struct中,都是int型
- 设置map容器,将关键字first设置为实力,值second设置为id,则每次输入一组id和实力到map容器时,会自动根据first的大小从小到大排序
- 初始先将facer的id和实力输入到map模型中
- 每次输入一组id和实力,因为选择的是实力最接近的,用lower_bound函数就能选中比当前实力小的最近的那个会员。
- 因为此时输入的这一会员没有插入到map容器中,所以对上一步选中的迭代器p进行判断。
- 如果p指向的是begin(),则说明此时没有比输入的实力小的会员,begin()就是最接近输入的,输出begin()的id。
- 如果p指向的是end(),则说明此时没有比输入的实力大的会员,那么end()-1就是最接近输入的,输出end()-1的id
- 其他情况,p指向的就是大于输入的实力的最小,对p--指向的就是小于输入实力的最大。进行差值的比较,输出差值小的那个;如果差值相同,由实例输出可以看出,选择输出的是id小的那个。
- 对这一会员进行输出结束后,将其插入map容器,进行下一个会员的输入判断
代码:
#include <iostream>
#include <map> //map容器头文件
using namespace std;
struct member
{
int id;
int power;
};
int main()
{
map<int, int> mp; //定义一个map容器mp,first是power,second是id
map<int, int>::iterator p; //定义一个迭代器p
mp[1000000000] = 1; //输入facer的信息到map,按关键字实力power排序
int n=0, id1=0, power1=0;
cin >> n;
for(int i=0; i<n; i++)
{
cin >> id1 >> power1;
p = mp.lower_bound(power1); //找大于等于power1的最小的实力
if( p == mp.begin() )
cout << id1 << " " << p->second << endl;
else if( p == mp.end() )
{
p --; //p指向的mp.end()不在集合里
cout << id1 << " " << p->second << endl;
}
//p指向的是大于power1的实力最接近的那个,可能存在实力比power1小的但是差距相同或更小
else
{
int id2, power2; //保存p指向的(大于)
int id3, power3; //保存p--指向的(小于)
id2 = p->second;
power2 = p->first;
p--;
id3 = p->second;
power3 = p->first;
//如果相等,那么输出的是id小的那个,也就是id3
if( (power2-power1) >= (power1-power3) )
cout << id1 << " " << id3 << endl;
else
cout << id1 << " " << id2 << endl;
}
mp[power1]=id1; //输出完成,将该会员保存到map容器中
}
return 0;
}
-
冷血格斗场
描述
为了迎接08年的奥运会,让大家更加了解各种格斗运动,facer新开了一家冷血格斗场。格斗场实行会员制,但是新来的会员不需要交入会费,而只要同一名老会员打一场表演赛,证明自己的实力。
我们假设格斗的实力可以用一个非负整数表示,称为实力值,两人的实力值可以相同。另外,每个人都有一个唯一的id,也是一个正整数。为了使得比赛更好看,每一个新队员都会选择与他实力最为接近的人比赛,即比赛双方的实力值之差的绝对值越小越好,如果有多个人的实力值与他差别相同,则他会选择id最小的那个。
不幸的是,Facer一不小心把比赛记录弄丢了,但是他还保留着会员的注册记录。现在请你帮facer恢复比赛纪录,按照时间顺序依次输出每场比赛双方的id。
输入
第一行一个数n(0 < n <=100000),表示格斗场新来的会员数(不包括facer)。以后n行每一行两个数,按照入会的时间给出会员的id和实力值。一开始,facer就算是会员,id为1,实力值1000000000。
输出
N行,每行两个数,为每场比赛双方的id,新手的id写在前面。
样例输入
3 2 3 3 1 4 2样例输出
2 1 3 2 4 2
分析:
- 与热血格斗场相似,但是冷血格斗场的实力power是非负整数(和正整数的区别在于多了一个0),且power可以重复,所以相同的power可以有多个id,那么如果用multimap进行排序(map不能有重复),关键词是power,id就需要遍历找到最小的那个。
- 介于上述原因,使用map容器来保存会员id与实力。其中,关键字first设置为int型的实力power,值second设置为set<int>型的,那么每次插入到map容器中时,用insert将对应的id插入到set容器中。
- 额外使用一个set容器来存储相同power的不同id,如此set容器中的id默认从小到大排序,那么当选中的power有多个id时,可以通过begin()快速输出对应id。
代码:
#include <iostream>
#include <map> //map容器头文件
#include <set> //set容器头文件
using namespace std;
// struct member
// {
// int id;
// int power;
// };
int main()
{
map<int, set<int>> mp; //定义一个map容器mp,first是power,second是set<id>
map<int, set<int>>::iterator p, p1; //定义迭代器p,p1
mp[1000000000].insert(1); //插入facer的信息到map,注意map的second是set容器,只能用insert插入
int n=0, id1=0, power1=0;
cin >> n;
for(int i=0; i<n; i++)
{
cin >> id1 >> power1;
p = mp.lower_bound(power1); //找大于等于power1的最小的实力
if( p == mp.begin() ) //与热血格斗场不同的是,second是set,输出的是set容器中的begin(),因为这样指示的是一个指针,所以要用指针的指针取值
cout << id1 << " " << *(p->second.begin()) << endl;
else if( p == mp.end() )
{
p --; //指向的mp.end()不在集合里
cout << id1 << " " << *(p->second.begin()) << endl;
}
else //p指向的是大于power1的实力最接近的那个,可能存在实力比power1小的但是差距相同
{
int id2, power2; //保存p指向的(大于)
int id3, power3; //保存p--指向的(小于)
id2 = *(p->second.begin());
power2 = p->first;
p--;
id3 = *(p->second.begin());
power3 = p->first;
if( (power2-power1) > (power1-power3) ) //与power3差距小,所以输出id3
cout << id1 << " " << id3 << endl;
else if( (power2-power1) < (power1-power3) ) //与power2差距小,所以输出id2
cout << id1 << " " << id2 << endl;
else //如果差距相同,则要比较两者的set<int>中的begin()谁更小,输出更小的那个
{
if( id2 < id3 )
cout << id1 << " " << id2 << endl;
else
cout << id1 << " " << id3 << endl;
}
}
mp[power1].insert(id1); //输出完成,将该会员保存到map容器中,注意格式
}
return 0;
}