【STL】 multimap + map / 热血格斗场+冷血格斗场

  • 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

举例子说明:

  1. 由下表可知,当集合中没有小于输入的score的分数时,lower_bound(score)的返回值是指向大于等于score的最小值;又因为集合中按分数从小到大排列,所以这个最小值就是当前集合的begin()
  2. 除此之外的情况,要么指向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的区别类似)
  1. map不能有关键字重复的元素(first重复)
  2. map可以使用[ ],下标为关键字,返回值为first和关键字相同的元素的second
  3. 插入元素可能失败(原先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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++ STL中的mapmultimap是关联容器,用于存储键值对(key-value pairs),其中每个键(key)唯一对应一个值(value)。 map是一个有序容器,根据键的大小进行自动排序,默认按照键的升序进行排序。每个键只能在map中出现一次,如果尝试插入具有相同键的元素,新元素将替代旧元素。 multimap也是一个有序容器,与map不同的是,它允许多个具有相同键的元素存在。多个具有相同键的元素将按照插入的顺序进行存储,而不会自动排序。 这两个容器都提供了一系列的操作函数,如insert、erase、find等,用于插入、删除和查找元素。 以下是一个使用map的简单示例: ```cpp #include <iostream> #include <map> int main() { std::map<std::string, int> scores; scores.insert(std::make_pair("Alice", 90)); scores.insert(std::make_pair("Bob", 80)); scores.insert(std::make_pair("Charlie", 70)); // 查找并输出Bob的分数 std::cout << "Bob's score: " << scores["Bob"] << std::endl; // 遍历并输出所有键值对 for (const auto& pair : scores) { std::cout << pair.first << ": " << pair.second << std::endl; } return 0; } ``` 上述示例中,我们创建了一个存储string类型键和int类型值的map容器scores。通过insert函数依次插入了三个键值对。然后我们通过scores["Bob"]来获取Bob的分数,并输出结果为80。 接着我们使用范围-based for循环遍历map中的所有键值对,并输出每个键值对的键和值。 multimap的用法与map类似,只是它允许多个具有相同键的元素存在。 这些关联容器在查找和插入操作上具有较高的效率,特别适用于需要根据键进行快速查找的景。在实际应用中,你可以根据自己的需求选择适合的容器类型。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值