【洛谷刷题 | 第三天】

本系列文章我将总结我在刷算法题所用到的知识,如果你也在刷算法并且是新手,我相信这系列文章会很适合你。

【洛谷刷题 | 第三天】

今日题目:

1. [CCC 2026 J4] Snail Path(模拟,STL)

链接:P15539 [CCC 2026 J4] Snail Path

记模拟蜗牛在无限网格上的移动,统计它进入已经黏滑的方格的次数(初始位置 (0,0) 默认黏滑,第一次进入不算;后续每一步移动到已黏滑的方格,计数 + 1)。

案例:

cpp 复制代码
输入                                     输出
3                                        4
S2
N2
S3

这道题主要是用到了用set<pair<int,int>>来存储所有黏滑方格,因为 set 本来就有自动去重、支持快速查重的特性,我当时想的是 "S2" 必须用 string 一起输入,但后面了解到可以用 char 和 int 分别输入,那这样,我们就可以根据方向,来具体判断每个方向对应的坐标单步增量 ------ 比如遇到 'N' 就把垂直方向的增量 y 设为 1,'S' 则设为 - 1,'E' 把水平方向增量 x 设为 1,'W' 设为 - 1,且每次处理新的移动指令时都会重置 x 和 y 为 0,避免不同方向的增量相互叠加导致移动逻辑出错。(这里细节特别注意,当时我就在这卡了很久)。

确定好单步增量后,就需要逐格模拟蜗牛的移动过程:每走一步就更新当前的坐标(dx 累加 x、dy 累加 y),接着用set的count方法检查当前坐标是否已经在黏滑方格集合中 ------ 如果存在,说明这一步进入了已黏滑的方格,就把统计重复次数的 sum 加 1;无论是否重复,都要把当前坐标插入到 set 中,标记为黏滑方格,供后续移动步骤检查。

题解:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
char a;
int dy,dx,sum,b;
int main()
{
    int n;
    cin>>n;
    set<pair<int,int>> s{{0,0}};
    
    for(int i=0;i<n;i++){
        cin>>a>>b;
        int x = 0, y = 0;
        if(a=='S') y=-1;
        if(a=='N') y=1;
        if(a=='W') x=-1;
        if(a=='E') x=1;
        while(b--){
            dy+=y,dx+=x;
            if(s.count({dx,dy}))
                sum++;
            s.insert({dx,dy});
        }
    }
    cout<<sum;
}
知识点:pair,count

1.pair

pair 是 STL 中最基础的 "数据打包工具",专门用来把两个任意类型的值绑定在一起(比如坐标 (x,y)、学号 + 成绩、方向 + 步数),本身无任何映射 / 查找 / 排序逻辑,仅做 "数据封装"。

定义格式:pair<类型1, 类型2> 变量名

cpp 复制代码
pair<int, int> p(3, 5);
cout << p.first;  // 输出3(第一个元素)
cout << p.second; // 输出5(第二个元素)

核心访问方式:通过 first/second 访问两个元素:

pair 只有两个固定成员:first 对应第一个元素,second 对应第二个元素,无下标 / 迭代器访问。

cpp 复制代码
pair<int, int> pos(0, 0); // 存储坐标(int+int)
pair<int, string> stu(1001, "张三"); // 存储学号+姓名(int+string)

常用初始化 / 赋值方式:

直接初始化:pair<int, string> p(1, "a");

赋值初始化:pair<int, string> p; p = {1, "a"};

和其他关键区别:

与 map 区分:pair 是 "单个二元组",map 是 "存储多组 pair 的映射容器";

比如:map<int, string> 里的每个元素都是 pair<const int, string>,但 pair 可独立使用,和 map 无隶属关系。

与数组区分:pair 只能存两个元素,类型可不同(如 int+string);数组可存多个元素,但类型必须统一。

2.count:

count() 是 STL容器(set/map/unordered_set/unordered_map 等) 的核心成员函数,核心作用可总结为:

对于 set/unordered_set(无重复元素容器):

返回值只有两种可能 ------0(元素不存在)或 1(元素存在),本质是 "快速查重";

对于 map/unordered_map(键唯一容器):

统计指定键的出现次数,返回值也是 0 或 1,比如 mp.count(5) 是判断键 5 是否存在于 map 中;
对于 multiset/multimap(允许重复元素容器):

返回值是元素 / 键的实际出现次数(比如 multiset 中存了 3 个 5,count(5) 返回 3)。

2.查找不重复元素出现的位置(二分)

链接:B2166 查找不重复元素出现的位置(二分)

给定一个严格递增的数列(长度 n),进行 m 次询问;每次询问一个数 q,输出 q 在数列中的下标(从 1 开始),不存在则输出 - 1;数据量极大(n/m≤1e6),需保证效率和 IO 速度。

案例:

cpp 复制代码
输入                                      输出
5 4                                       3
10 20 30 40 50                            1
30                                        5
10                                        -1
50
35

这道题主要用到了简单的二分知识,通过利用数列 "严格递增" 的特性,将原本暴力遍历 O (n) 的查找复杂度优化到 O (logn),避免了超时问题。

题解:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int a[1000001];
int n,m;
int ok(int x){
    int l = 1,r = n;
    while(l<=r){
        int p = l+(r-l)/2;
        if(a[p] == x){
            return p;
        }
        else if(a[p]<x){
            l = p+1;
        }
        else if(a[p]>x){
            r = p-1;
        }
    }
    return -1;
};
int main()
{
   
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    while(m--){
        int b;
       scanf("%d",&b);
            printf("%d\n",ok(b));
        
    }
}
知识点:二分

什么是二分:

二分查找是一种针对有序数列的高效查找算法,核心思想是分治------ 把查找范围每次缩小一半,直到找到目标或确定目标不存在。

可以用一个通俗的例子理解:

找一本页码 1~100 的书里的第 37 页,不用从第 1 页翻到第 37 页,而是先翻到中间(50 页),发现 50>37,就只看前 50 页;再翻前 50 页的中间(25 页),25<37,就只看 25~50 页;重复这个过程,几次就能找到 37 页。

怎么做:

步骤 1:初始化查找边界

左边界 le:从数列的起始位置开始(题目下标从 1 开始则le=1,从 0 开始则le=0);

右边界r:到数列的结束位置结束(即数列长度n)。
步骤 2:循环缩小查找范围

循环条件:le <= r(只要左右边界不交叉,就还有查找空间)。

每次循环做 3 件事: 计算中间位置 mid = le + (r -le) / 2; 对比a[mid]和目标值q: 若 a[mid] ==q:找到目标,直接返回mid; 若 a[mid] < q:目标在右半区,把左边界更新为le = mid + 1; 若

a[mid] > q:目标在左半区,把右边界更新为r = mid - 1; 重复循环,直到找到目标或循环结束。
步骤 3:处理未找到的情况

如果循环结束(le > r)还没找到目标,说明数列中没有该数,返回 - 1。

什么时候用:

前提:必须要满足有序 / 有界 + 可缩小范围

找某个数的位置:如 "有序数列中找 q 的下标,不存在返回 - 1";

找边界值:如 "有序数列中第一个大于 q 的数""最后一个小于 q 的数";

判断数是否存在:如 "有序数组中是否包含 q"。

最后:

如果我的内容对你有帮助,请点赞,评论,收藏。创作不易,大家的支持就是我坚持下去的动力!

相关推荐
abant22 小时前
leetcode912 排序算法总结
算法·leetcode·排序算法
@猿程序2 小时前
ShardingSphere自定义分片算法与Redis动态规则加载实战
网络·redis·算法
Share_Shun2 小时前
【定位引导】多点对位算法
算法
炽烈小老头2 小时前
【 每天学习一点算法 2026/03/18】全排列
学习·算法
Book思议-2 小时前
【数据结构实战】判断链表是否有环:快慢指针法(Floyd 判圈算法)
c语言·数据结构·算法·链表
liuyao_xianhui2 小时前
优选算法_位运算_只出现一次的数字3_C++
开发语言·数据结构·c++·算法·leetcode·链表·动态规划
lihao lihao2 小时前
滑动窗口
数据结构·算法
Jordannnnnnnn2 小时前
复试打卡day30
算法
郝学胜-神的一滴2 小时前
贪心策略实战Leetcode 860题:柠檬水找零问题的优雅解法
数据结构·c++·算法·leetcode·职场和发展