蓝桥杯抱佛脚第一天|简单模拟,set,map的使用

题单:【蓝桥杯】蓝桥杯省一秘诀(国赛题单看简介) - 题单 - 洛谷 | 计算机科学教育新生态

状态:嗯嗯今天掌握的不错,set和map确实很方便,需要多看多练

P1102 A-B数对

题目链接:P1102 A-B 数对 - 洛谷

题目描述

给出一串正整数数列以及一个正整数 C ,要求计算出所有满足 AB =C 的数对的个数(不同位置的数字一样的数对算不同的数对)。

输入格式

输入共两行。

第一行,两个正整数 N ,C

第二行,N 个正整数,作为要求处理的那串数。

输出格式

一行,表示该串正整数中包含的满足 AB =C 的数对的个数。

输入输出样例

输入:

复制代码
4 1
1 1 2 3

输出:

复制代码
3

说明/提示

对于 75% 的数据,1≤N≤2000。

对于 100% 的数据,1≤N ≤2×105,0≤a**i <230,1≤C<230。

2017/4/29 新添数据两组

这道题刚看的想法当然是模拟了,按照题意模拟,统计个数。然后发现样例并不能全部通过,嗯......因为超时了。

复制代码
//模拟
#include<bits/stdc++.h>
using namespace std;
int main(){
    int n,c;
    cin>>n>>c;
    vector<int> number(n);
    for(int i=0;i<n;i++){
        cin>>number[i];
    }
    int count=0;
    for(int j=0;j<n;j++){
        for(int k=0;k<n;k++){
            if(number[j]-number[k]==c){
                count++;
            }
        }
    }
    cout<<count<<endl;
    return 0;
}

这道题可以用unordered_map,这样就可以全部通过了,是的俺之前压根不会用哈希,这次需要学习一番,set,map的介绍在后面!可以把A-B=C变换成A=B+C,这样就只需要枚举一个B。

复制代码
//优化版
#include<bits/stdc++.h>
using namespace std;
int main() {
    int n, c;
    cin >> n >> c;
    vector<int> number(n);  // 预先分配大小
    unordered_map<int, int> freq;//map是键值对 
    for(int i = 0; i < n; i++) {
        cin >> number[i];
        freq[number[i]]++;//这里就是把输入的值为键,映射到值上
    }    
    long long count = 0;    
    // 枚举每个数作为B,找A = B + C
    for(int i = 0; i < n; i++) {
        int a = number[i] + c;
        count += freq[a];  // 统计A出现的次数
    }    
    cout << count << endl;
    return 0;
}

P1918 保龄球

题目链接:P1918 保龄球 - 洛谷

题目描述

DL 算缘分算得很烦闷,所以常常到体育馆去打保龄球解闷。因为他保龄球已经打了几十年了,所以技术上不成问题,于是他就想玩点新花招。

DL 的视力真的很不错,竟然能够数清楚在他前方十米左右每个位置的瓶子的数量。他突然发现这是一个炫耀自己好视力的借口------他看清远方瓶子的个数后从某个位置发球,这样就能打倒一定数量的瓶子。

  1. ◯◯◯

  2. ◯◯◯ ◯

  3. ◯ ◯

如上图,每个 "◯" 代表一个瓶子。如果 DL 想要打倒 3 个瓶子就在 1 位置发球,想要打倒 4 个瓶子就在 2 位置发球。

现在他想要打倒 m 个瓶子。他告诉你每个位置的瓶子数,请你给他一个发球位置。

输入格式

第一行包含一个正整数 n,表示位置数。

第二行包含 n 个正整数 a**i ,表示第 i 个位置的瓶子数,保证各个位置的瓶子数不同。

第三行包含一个正整数 Q,表示 DL 发球的次数。

第四行至文件末尾,每行包含一个正整数 m ,表示 DL 需要打倒 m 个瓶子。

输出格式

Q 行。每行包含一个整数,第 i 行的整数表示 DL 第 i 次的发球位置。若无解,则输出 0。

输入输出样例

输入:

复制代码
5
1 2 4 3 5
2
4
7

输出:

复制代码
3
0

说明/提示

【数据范围】

对于 50% 的数据,1≤n ,Q ≤1000,1≤a**i ,m≤105。

对于 100% 的数据,1≤n ,Q ≤100000,1≤a**i ,m≤109。

这道题我刚开始照样还是用了模拟,哈哈哈照样没有全部通过,还是因为超时了。嗯......需改进。

复制代码
//模拟
#include<bits/stdc++.h>
using namespace std;
int main(){
    int n,q;
    long long m;
    cin>>n;
    vector<int> number(n);
    for(int i=0;i<n;i++){
        cin>>number[i];
    }
    int flag=0;
    cin>>q;
    for(int j=0;j<q;j++){
        cin>>m;
        flag=0;
        for(int k=0;k<n;k++){
            if(number[k]==m){
                flag=1;
                cout<<k+1<<endl;
                break;
            }
        }
        if(flag==0){
            cout<<0<<endl;
        }
    }
    return 0;
}

其实这道题照样还是要用unordered_map来解决,会效率高很多,如果不知道unordered_map是什么,请往下看。

复制代码
//优化版
#include <bits/stdc++.h>
using namespace std;
int main() {
    int n, q;
    cin >> n;
    unordered_map<int, int> pos;  // 瓶子数 → 位置(1-based)
    for (int i = 0; i < n; i++) {
        int val;
        cin >> val;
        pos[val] = i + 1;  //从1开始
    }
    cin >> q;
    while (q--) {
        int m;
        cin >> m;
        if (pos.count(m)) {//如果找得到,这里面有m为键的值,就输出其位置
            cout << pos[m] << endl;
        } else {
            cout << 0 << endl;
        }
    }
    return 0;
}

P5266 【深基17.例6】学籍管理

题目链接:P5266 【深基17.例6】学籍管理 - 洛谷

题目描述

您要设计一个学籍管理系统,最开始学籍数据是空的,然后该系统能够支持下面的操作(不超过 105 条):

  • 插入与修改,格式 1 NAME SCORE:在系统中插入姓名为 NAME(由字母和数字组成不超过 20 个字符的字符串,区分大小写),分数为 SCORE(0<SCORE<231) 的学生。如果已经有同名的学生则更新这名学生的成绩为 SCORE。如果成功插入或者修改则输出 OK

  • 查询,格式 2 NAME:在系统中查询姓名为 NAME 的学生的成绩。如果没能找到这名学生则输出 Not found,否则输出该生成绩。

  • 删除,格式 3 NAME:在系统中删除姓名为 NAME 的学生信息。如果没能找到这名学生则输出 Not found,否则输出 Deleted successfully

  • 汇总,格式 4:输出系统中学生数量。

输入格式

第一行,输入一个正整数 Q (1≤Q≤105),表示操作数量。

接下来 Q 行,每行先输入一个正整数 o**po**p∈[1,4]),表示操作种类。接着:

  • 如果 o**p=1,则再输入一个字符串 NAME 以及一个正整数 SCORE,含义见题目描述。

  • 如果 o**p=2,则再输入一个字符串 NAME,含义见题目描述。

  • 如果 o**p=3,则再输入一个字符串 NAME,含义见题目描述。

  • 如果 o**p=4,则无需再输入其他内容。

输出格式

共输出 Q 行,每行输出一个字符串或正整数,为对应操作的处理结果,具体含义见题目描述。

输入输出样例

输入:

复制代码
5
1 lxl 10
2 lxl
3 lxl
2 lxl
4

输出:

复制代码
OK
10
Deleted successfully
Not found
0

嗯嗯,这道题用map解决会方便很多,因为一是两个值的映射,一个name,一个score,二是用map的函数会简单很多捏。

复制代码
#include <bits/stdc++.h>
using namespace std;
int main() {
    int Q;
    cin >> Q;
    // 键是学生姓名(string),值是学生成绩(long long)
    map<string, long long> students;
    while (Q--) {
        int op;
        string name;
        long long score;
        cin >> op;
        if (op == 1) { // 插入或修改
            cin >> name >> score;
            // 无论是否存在,直接使用 [] 赋值即可
            // 如果存在则更新,不存在则插入
            students[name] = score;
            cout << "OK\n";
        }
        else if (op == 2) { // 查询
            cin >> name;
            auto it = students.find(name);
            if (it != students.end()) {//这里是迭代器,就相当于指针,如果找到了,就返回这个位置的迭代器,否则就返回end()
                // it->second 访问值(成绩)
                cout << it->second << '\n';
            } else {
                cout << "Not found\n";
            }
        }
        else if (op == 3) { // 删除
            cin >> name;
            auto it = students.find(name);
            if (it != students.end()) {
                students.erase(it); // 或 students.erase(name)删除操作
                cout << "Deleted successfully\n";
            } else {
                cout << "Not found\n";
            }
        }
        else { // op == 4,汇总
            cout << students.size() << '\n';
        }
    }
    return 0;
}

P5250 【深基17.例5】木材仓库

题目链接:P5250 【深基17.例5】木材仓库 - 洛谷

题目描述

博艾市有一个木材仓库,里面可以存储各种长度的木材,但是保证没有两个木材的长度是相同的。作为仓库负责人,你有时候会进货,有时候会出货,因此需要维护这个库存。有不超过 105 条的操作:

  • 进货,格式 1 Length:在仓库中放入一根长度为 Le ngt**h (不超过 109)的木材。如果已经有相同长度的木材那么输出 Already Exist

  • 出货,格式 2 Length:从仓库中取出长度为 Le ngt**h 的木材。如果没有刚好长度的木材,取出仓库中存在的和要求长度最接近的木材。如果有多根木材符合要求,取出比较短的一根。输出取出的木材长度。如果仓库是空的,输出 Empty

输入格式

第一行一个数 m 代表操作次数。

接下来 m 行,每行一次操作,格式如题目描述所示。

输出格式

对于每次操作,按照题目描述要求输出答案。

输入输出样例

输入:

复制代码
7
1 1
1 5
1 3
2 3
2 3
2 3
2 3

输出:

复制代码
3
1
5
Empty

这道题是用set来解决,也是一个容器,我本来是拿vector写的,确实很麻烦,还是set好。这道题用set主要是满足了这两点,元素自动从小到大排序,元素唯一,不能重复。

复制代码
#include <bits/stdc++.h>
using namespace std;
int main() {
    int m;
    cin >> m;
    set<long long> warehouse;
    while (m--) {
        int op;
        long long len;
        cin >> op >> len;
        if (op == 1) { // 进货
            if (warehouse.find(len) != warehouse.end()) {
                cout << "Already Exist\n";
            } else {
                warehouse.insert(len);
            }
        } else { // 出货
            if (warehouse.empty()) {
                cout << "Empty\n";
                continue;
            }
            auto it = warehouse.lower_bound(len);
            long long result;
            if (it == warehouse.end()) {
                // 没有比 len 大的,取最大的
                --it;
                result = *it;
                warehouse.erase(it);
            } else if (it == warehouse.begin()) {
                // 最小的都比 len 大或等于,取最小的
                result = *it;
                warehouse.erase(it);
            } else {
                // 有两个候选:*it 和 *prev(it)
                auto prev_it = prev(it);
                long long diff1 = *it - len;      // 正数或0
                long long diff2 = len - *prev_it; // 正数
                if (diff2 <= diff1) {
                    // 前一个更近或一样近(一样近取短的,即前一个)
                    result = *prev_it;
                    warehouse.erase(prev_it);
                } else {
                    result = *it;
                    warehouse.erase(it);
                }
            }
            cout << result << '\n';
        }
    }
    return 0;
}

set容器

1. set 容器简介

set 是 C++ STL 中的有序集合容器:

  • 元素自动从小到大排序

  • 元素唯一,不能重复

  • 底层通常用红黑树 实现,插入、删除、查找都是 O(log n)

2. 代码中用到的 set 相关操作

warehouse.find(len)

复制代码
if (warehouse.find(len) != warehouse.end())
  • 功能 :在 set 中查找值为 len 的元素

  • 返回值 :如果找到,返回指向该元素的迭代器 ;如果没找到,返回 end()

  • 用途 :判断长度为 len 的木材是否已存在

warehouse.insert(len)

复制代码
warehouse.insert(len);
  • 功能:向 set 中插入一个元素

  • 特点:如果元素已存在,插入失败(但不会报错)

  • 返回值 :返回一个 pair<iterator, bool>,其中 bool 表示是否插入成功

warehouse.empty()

复制代码
if (warehouse.empty());
  • 功能:判断 set 是否为空

  • 返回值 :空返回 true,否则 false

warehouse.lower_bound(len)

复制代码
auto it = warehouse.lower_bound(len);

这是本题最关键的函数!

  • 功能 :返回第一个不小于 len 的元素的迭代器

  • 理解 :就是二分查找的 lower_bound

  • 示例

    • 如果 set 中有 {2, 5, 7, 10}lower_bound(5) 返回指向 5 的迭代器

    • lower_bound(6) 返回指向 7 的迭代器

    • lower_bound(11) 返回 end()

迭代器操作

复制代码
--it;                    // 迭代器前移(指向更小的元素)
auto prev_it = prev(it); // 获取前一个迭代器(C++11起可用)
  • 迭代器 可以像指针一样使用 ++-- 操作

  • prev(it) 函数返回 it 的前一个迭代器(需要 <iterator> 头文件,但 <bits/stdc++.h> 已包含)

warehouse.erase(it)

复制代码
warehouse.erase(it);
warehouse.erase(prev_it);
  • 功能:删除迭代器指向的元素

  • 参数 :可以是迭代器,也可以是具体的值(如 warehouse.erase(len)

  • 返回值:返回被删除元素的下一个迭代器(如果用迭代器删除)

3. 其他相关 set 函数(本题没用但常用)

复制代码
// 上界:第一个大于 len 的元素
auto it = warehouse.upper_bound(len);
// 获取第一个/最后一个元素
auto first = *warehouse.begin();
auto last = *warehouse.rbegin();  // 或 *prev(warehouse.end())
// 清空所有元素
warehouse.clear();
// 获取元素个数
int size = warehouse.size();

map容器

1.map 是什么?

map 是 C++ STL 中的关联容器 ,存储键值对(key-value pair):

  • 每个键唯一,不能重复

  • 自动按键排序(默认升序)

  • 底层用红黑树 实现,插入、删除、查找都是 O(log n)

复制代码
map<string, long long> students;
// 键类型:string(学生姓名)
// 值类型:long long(学生成绩)

2. 本题用到的 map 操作

students[name] = score
复制代码
students[name] = score;
  • 功能:插入或修改学生成绩

  • 特点

    • 如果 name 不存在,插入新键值对

    • 如果 name 已存在,更新对应的值

    • 非常方便,一步完成"插入与修改"操作

students.find(name)
复制代码
auto it = students.find(name);
if (it != students.end()) {
    // 找到了
}
  • 功能 :查找键为 name 的元素

  • 返回值 :返回指向该元素的迭代器 ;如果没找到,返回 end()

  • 迭代器 指向一个 pair<const Key, T>,可以通过 it->first 访问键,it->second 访问值

students.erase(it)students.erase(name)
复制代码
students.erase(it);      // 用迭代器删除
students.erase(name);    // 直接用键删除
  • 功能:删除指定元素

  • 两种形式:接受迭代器或键

  • 返回值(如果用键删除):返回被删除的元素个数(0 或 1)

students.size()
复制代码
cout << students.size() << '\n';
  • 功能 :返回 map 中元素的个数

  • 时间复杂度:O(1)

3. map 的其他常用操作(本题没用但常用)

// 判断是否为空

if (students.empty()) { ... }

// 清空所有元素

students.clear();

// 遍历所有学生

for (auto& p : students) {

cout << p.first << ": " << p.second << endl;

}

// 使用迭代器遍历

for (auto it = students.begin(); it != students.end(); ++it) {

cout << it->first << ": " << it->second << endl;

}

// 统计某个键出现的次数(对map来说只能是0或1)

int count = students.count(name);

// 插入的另一种方式

students.insert({name, score});

// 或 students.insert(make_pair(name, score));

相关推荐
仟濹2 小时前
【算法打卡day27(2026-03-19 周四)】蓝桥云课中Lv.1难度中的绝大部分题
算法·蓝桥杯
罗湖老棍子2 小时前
滑动窗口与双调队列:幕布覆盖问题(定右缩左满分板子)改编自LeetCode 1438
算法·滑动窗口·单调队列
CoovallyAIHub2 小时前
ICLR 2026 | MedAgent-Pro:用 Agent 工作流模拟临床医生的循证诊断过程
深度学习·算法·计算机视觉
实心儿儿2 小时前
算法7:两个数组的交集
算法·leetcode·职场和发展
我可能是个假开发2 小时前
算法-回溯
算法
WolfGang0073212 小时前
代码随想录算法训练营 Day14 | 二叉树 part04
数据结构·算法
爱丽_2 小时前
GC 怎么判定“该回收谁”:GC Roots、可达性分析、四种引用与回收算法
java·jvm·算法
dfafadfadfafa2 小时前
嵌入式C++安全编码
开发语言·c++·算法
仍然.2 小时前
算法题目---前缀和
算法