蓝桥杯练习题——双指针

文章介绍了六个编程问题的解决方案,涉及排序数组、寻找最长连续不重复子序列、数组元素目标和、判断子序列、日志统计中的热帖检测以及统计子矩阵的前缀和。每个问题都采用了不同的数据结构和算法技巧来高效求解。
摘要由CSDN通过智能技术生成

1.牛的学术圈 I

在这里插入图片描述
在这里插入图片描述

思路

1.从大到小排序,把数组分成三段,[1, j] (j, j2] (j2, n],j 以 i 为界限,j2 以 i - 1 为界限
2.第一部分引用数已经够了,第三部分引用数差太多,判断第一部分 + min(L, 第二部分)是否有 i 篇

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e5 + 10;
int a[N];
int n, l;

int main(){
    scanf("%d%d", &n, &l);
    for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
    sort(a + 1, a + n + 1, greater<int>());
    int res = 0;
    for(int i = 1, j = n, j2 = n; i <= n; i++){
        // 维护大于等于 i 和小于 i 的界限
        while(j && i > a[j]) j--;
        // 维护大于等于 i - 1 和小于 i - 1 的界限
        while(j2 && i - 1 > a[j2]) j2--;
        // 判断第一部分 + min(L, 第二部分)是否有 i 篇
        if(j + min(l, j2 - j) >= i){
            res = i;
        }
    }
    printf("%d", res);
    return 0;
}

2.最长连续不重复子序列

在这里插入图片描述

思路

用 i 和 j 维护一组连续不重复子序列,用一个数组存储这个区间每个元素出现了多少次,如果出现次数大于 1,那就有重复元素,i 右移直到没有重复元素

#include<iostream>
using namespace std;
const int N = 1e5 + 10;
int a[N], b[N];
int n;

int main(){
    scanf("%d", &n);
    for(int i = 0; i < n; i++) scanf("%d", &a[i]);
    int res = 0;
    for(int i = 0, j = 0; j < n; j++){
        b[a[j]]++;
        while(b[a[j]] > 1){
            b[a[i]]--;
            i++;
        }
        res = max(res, j - i + 1);
    }
    printf("%d", res);
    return 0;
}

3.数组元素的目标和

在这里插入图片描述
在这里插入图片描述

思路

i 指针正序遍历 a 数组,j 指针倒序遍历 b 数组,如果和大了 j 左移,和小了 i 右移

#include<iostream>
using namespace std;
const int N = 1e5 + 10;
int n, m, x;
int a[N], b[N];

int main(){
    scanf("%d%d%d", &n, &m, &x);
    for(int i = 0; i < n; i++) scanf("%d", &a[i]);
    for(int i = 0; i < m; i++) scanf("%d", &b[i]);
    int l = 0, r = m - 1;
    while(1){
        if(a[l] + b[r] == x){
            printf("%d %d", l, r);
            break;
        }
        while(a[l] + b[r] < x) l++;
        while(a[l] + b[r] > x) r--;
    }
    return 0;
}

4.判断子序列

在这里插入图片描述
在这里插入图片描述

思路

i 指针枚举 a 数组,j 指针枚举 b 数组,j 指针一直右移,如果两个位置相等,i 指针右移

#include<iostream>
using namespace std;
const int N = 1e5 + 10;
int a[N], b[N];
int n, m;

int main(){
    scanf("%d%d", &n, &m);
    for(int i = 0; i < n; i++) scanf("%d", &a[i]);
    for(int i = 0; i < m; i++) scanf("%d", &b[i]);
    int l = 0, r = 0;
    while(l < n && r < m){
        if(a[l] == b[r]) l++;
        r++;
    }
    printf("%s", (l == n ? "Yes" : "No"));
    return 0;
}

5.日志统计

在这里插入图片描述
在这里插入图片描述

思路

根据时间排序,可以独立处理编号或者并行处理编号
1.独立处理编号:用 cnt 数组存储每个编号得到的赞,如果时间大于等于 d 了,那么左指针右移,如果当前右指针的编号赞大于等于 k 了,那就是热帖
2.并行处理编号:每次判断长为 k 的区间是否是一个编号,如果是一个编号且时间小于 d,那么就是热帖,然后再移动到下个编号区间

// 独立处理编号
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e5 + 10;
pair<int, int> a[N];
int b[N], cnt[N];
int n, d, k;

int main(){
    scanf("%d%d%d", &n, &d, &k);
    int ts, id;
    for(int i = 0; i < n; i++){
        scanf("%d%d", &ts, &id);
        a[i] = make_pair(ts, id);
    }
    sort(a, a + n);
    for(int i = 0, j = 0; j < n; j++){
        int it = a[j].second;
        cnt[it]++;
        while(a[j].first - a[i].first >= d){
            cnt[a[i].second]--;
            i++;
        }
        if(cnt[it] >= k) b[it] = 1;
    }
    for(int i = 0; i <= 100000; i++){
        if(b[i]) printf("%d\n", i);
    }
    return 0;
}

// 并行处理编号
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e5 + 10;
pair<int, int> a[N];
int b[N], cnt[N];
int n, d, k;

int main(){
    scanf("%d%d%d", &n, &d, &k);
    int ts, id;
    for(int i = 0; i < n; i++){
        scanf("%d%d", &ts, &id);
        a[i] = make_pair(id, ts);
    }
    sort(a, a + n);
    int l = 0, r = k - 1;
    while(r < n){
    	// 是一个编号区间
        if(a[l].first == a[r].first){
            if(a[r].second - a[l].second < d){
                printf("%d\n", a[l].first);
                // 移动到下一个编号区间
                while(r < n && a[l].first == a[r].first) r++;
                l = r, r += k - 1;
            }else{
                l++, r++;
            }
        }else{ // 不是一个编号区间
            while(r < n && a[l].first == a[r].first) r++;
            l = r, r += k - 1;
        }
    }
    return 0;
}

6.统计子矩阵

在这里插入图片描述

思路

1.计算前缀和
2.枚举上下边界,双指针枚举左右边界,如果矩阵和大了,左指针就右移
3.注意 l <= r

#include<iostream>
using namespace std;
const int N = 510;
int a[N][N];
int n, m, k;

int main(){
    scanf("%d%d%d", &n, &m, &k);
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= m; j++){
            scanf("%d", &a[i][j]);
            a[i][j] += a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1];
        }
    }
    // i, l   i, r
    // j, l   j, r
    long long cnt = 0;
    for(int i = 1; i <= n; i++){
        for(int j = i; j <= n; j++){
            for(int l = 1, r = 1; r <= m; r++){
                while(l <= r && a[j][r] - a[j][l - 1] - a[i - 1][r] + a[i - 1][l - 1] > k) l++;
                if(l <= r) cnt += r - l + 1;
            }
        }
    }
    printf("%lld", cnt);
    return 0;
}
  • 8
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值