2023蓝桥杯省模拟赛附近最小

2023蓝桥杯省模拟赛附近最小

在这里插入图片描述

这个题算是一个经典的数据结构入门题了,写了几个解法水一篇文章

map维护

时间复杂度nlgn,但是常数比较大,所以只能过90%数据

#include <iostream>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
const ll mod = 1e9 + 7;
int a[N];
void solve()
{
    int n, k;
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    cin >> k;
    map<int, int>mp;
    int l = 1, r = min(k + 1, n);
    for (int i = 1; i <= r; i++)
        mp[a[i]]++;
    cout << mp.begin()->first;
    for (int i = 2; i <= n; i++) {
        int l1 = max(1, i - k), r1 = min(n, k + i);
        if (l1 > l)
            mp[a[l]]--;
        if (r1 > r)
            mp[a[r1]]++;
        if (mp[a[l]] == 0)
            mp.erase(a[l]);
        l = l1, r = r1;
        cout << " " << mp.begin()->first;
    }
    cout << '\n';
}
int  main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int T = 1;
    //    cin >> T;
    while (T--)
    {
        solve();
    }
    return 0;
}

单调队列

这算是题解区看到的最多的解法了,刚开始也没往这方面想,直接无脑贴了个线段树上去

代码里面有注释

时间复杂度是线性的,但是deque的常数是stl里面都算很大的,所以也比较慢,可以尝试用数组模拟deque

#include<iostream>
#include<deque>
using namespace std;
typedef long long ll;
const int N = 2e6 + 10;
int a[N];
int main()
{
    int n, k;
    cin >> n;
    deque<int> dq;
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    cin >> k;
    for (int i = n + 1; i <= n + k; i++)
        a[i] = 2 * N;
    for (int i = 1; i <= n + k; i++)
    {
        while (!dq.empty() && a[dq.back()] > a[i])
            dq.pop_back();
        dq.push_back(i);
        while (i - 2 * k > dq.front())
            dq.pop_front();
        if (i > k)  cout << a[dq.front()] << " ";
    }
    return 0;
}
//为了方便判断范围所以将数组的下标索引加入队列。
// //使用双端队列实现滑动窗口,窗口每次向后移动,若新的数字比队尾大,
//直接进入队列, 比队尾小,需要弹出队列中所有比新的数字小的值,
//保证队头保存的是当前窗口中最小的值
//通过维护一个单调递增的队列,可以得到一个滑动窗口的最小值,
//并将他加入队列。例如窗口[7, 4, 3] 的最小值就是 3 
//,那么对于题目要求的一定范围我们只需要每次输出前判断队首是否超过范围就行了,
//如果超过范围就将队首弹出,那么队列就只剩下[3] 了。

树状数组

把窗口中的数投到长度为1e6的数轴上,每投影一个数在数轴对应位置+1,然后转换成了求第一个数轴的前缀和大于等于1的点的位置,二分即可,当然这个前缀和也可以用线段树维护

这个解法的局限性在于a[i]的值域,如果值域过大,比如1e18,那肯定开不下这么大的数组,就需要离散化一下了

下面提供非离散化和离散化的代码

非离散化
#include <iostream>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
const ll mod = 1e9 + 7;
int tree[N], a[N];
int lowbit(int x) {
    return x & (-x);
}
int sum(int x) {
    int ans = 0;
    while (x) {
        ans += tree[x];
        x -= lowbit(x);
    }
    return ans;
}
void change(int x, int p) {
    while (p<N) {
        tree[p] += x;
        p += lowbit(p);
    }
}
void solve()
{
    int n,k;
    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    int mi = 0x3f3f3f3f;
    cin >> k;
    int l = 1, r = min(k + 1, n),x=0x3f3f3f3f;
    for (int i = 1; i <= r; i++)
        change(1, a[i]),mi=min(a[i],mi);
    cout << mi;
    for (int i = 2; i <= n; i++) {
        int l1 = max(1, i - k), r1 = min(n, k + i);
        if (l1 > l)
            change(-1, a[l]);
        if (r1 > r)
            change(1, a[r1]);
        int x = 1, y = 1e6 + 1;
        while (x < y) {
            int mid = x + y >> 1;
            if (sum(mid) >= 1)
                y = mid;
            else
                x = mid + 1;
        }
        l = l1, r = r1;
        cout << " " << y;
    }
    cout << '\n';
}
int  main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int T = 1;
    //    cin >> T;
    while (T--)
    {
        solve();
    }
    return 0;
}
离散化
#include <iostream>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
const ll mod = 1e9 + 7;
int tree[N], a[N],b[N];
int len = 0, n, k;
int lowbit(int x) {
    return x & (-x);
}
int sum(int x) {
    int ans = 0;
    while (x) {
        ans += tree[x];
        x -= lowbit(x);
    }
    return ans;
}
void change(int x, int p) {
    while (p<=n) {
        tree[p] += x;
        p += lowbit(p);
    }
}
int serach(int x) {
    int l = 1, r = len + 1;
    while (l < r) {
        int mid = l + r >> 1;
        if (b[mid] == x)
            return mid;
        else if (b[mid] < x)
            l = mid + 1;
        else
            r = mid;
    }
    return r;
}
void solve()
{
    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> a[i],b[i]=a[i];
    int mi = 0x3f3f3f3f;
    cin >> k;
    sort(b + 1, b + n + 1);
    for ( int j = 1; j <= n; j++) {
        if (b[j] != b[j-1])
            b[++len] = b[j];
    }
    int l = 1, r = min(k + 1, n),x=0x3f3f3f3f;
    for (int i = 1; i <= r; i++)
        change(1, serach(a[i])),mi=min(a[i],mi);
    cout << mi;
    for (int i = 2; i <= n; i++) {
        int l1 = max(1, i - k), r1 = min(n, k + i);
        if (l1 > l) {
            change(-1, serach(a[l]));
        }
        if (r1 > r)
            change(1, serach(a[r1]));
        int x = 1, y = len + 1;
        while (x < y) {
            int mid = x + y >> 1;
            if (sum(mid) >= 1)
                y = mid;
            else
                x = mid + 1;
        }
        l = l1, r = r1;
        cout << " " << b[y];
    }
    cout << '\n';
}
int  main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int T = 1;
    //    cin >> T;
    while (T--)
    {
        solve();
    }
    return 0;
}
类似题

力扣342周赛也考了个类似的题,也可以用树状数组求解

在这里插入图片描述

class Solution {
public:
int tree[110];
int lowbit(int x){
    return x&-x;
}
int sum(int x){
    int ans=0;
    while(x){
        ans+=tree[x];
        x-=lowbit(x);
    }
    return ans;
}
void change(int pos,int x){
    while(pos<=105){
        tree[pos]+=x;
        pos+=lowbit(pos);
    }
}
    vector<int> getSubarrayBeauty(vector<int>& a, int k, int x) {
        vector<int>ans;
        for(int i=0;i<k;i++)
        change(a[i]+51,1);
        int l=1,r=105;
        while(l<r){
            int mid=l+r>>1;
            if(sum(mid)>=x)
            r=mid;
            else
            l=mid+1;
        }
        if(r-51<0)
        ans.push_back(r-51);
        else 
        ans.push_back(0);
        for(int i=1;i+k-1<a.size();i++){
            change(a[i-1]+51,-1);
            change(a[i+k-1]+51,1);
            l=1,r=105;
        while(l<r){
            int mid=l+r>>1;
            if(sum(mid)>=x)
            r=mid;
            else
            l=mid+1;
        }
        if(r-51<0)
        ans.push_back(r-51);
        else 
        ans.push_back(0);
        }
        return ans;
    }
};

ST表

st表最经典的应用就是求解静态rmq问题了

下面用倍增跳表求解

#include <iostream>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
const ll mod = 1e9 + 7;
int st[N][21],lg2[N];
void solve()
{
    int n,k;
    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> st[i][0];
    for (int i = 2; i <= n; i++)
        lg2[i] = lg2[i / 2] + 1;
    cin >> k;
    for (int i = 1; (1 << i) <= n; i++) {
        for (int j = 1; j + (1 << i) - 1 <= n; j++)
            st[j][i] = min(st[j][i - 1], st[j + (1 << (i - 1))][i - 1]);
    }
    for (int i = 1; i <= n; i++) {
        int l = max(1, i - k), r = min(n, k + i);
        int len = r - l + 1,mi=0x3f3f3f3f;
        //cout << i << '\n';
        while (len) {
            mi = min(mi, st[l][lg2[len]]);
            l += (1 << lg2[len]);
            len -= (1 << lg2[len]);
        }
        cout << mi;
        if (i < n)
            cout << ' ';
        else
            cout << '\n';
    }
    //cout << '\n';
}
int  main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int T = 1;
    //    cin >> T;
    while (T--)
    {
        solve();
    }
    return 0;
}

线段树

线段树无脑维护就行了,只需要写查询

#include <iostream>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
int a[N];
struct tnode {
    int l, r;
    int sum;
};
struct Segment_Tree
{
    tnode t[4 * N];
    void build(int root, int l, int r, int* A)
    {
        t[root].l = l, t[root].r = r;
        if (l == r)
            t[root].sum = A[l];
        else {
            int ch = root << 1, mid = l + r >> 1;
            build(ch, l, mid, A);
            build(ch + 1, mid + 1, r, A);
            t[root].sum = min(t[ch].sum , t[ch + 1].sum);
        }
    }
    int query_min(int root, int l, int r)
    {
        if (l == t[root].l && r == t[root].r)
            return t[root].sum;
        int mid = t[root].l + t[root].r >> 1;
        if (r <= mid)
            return query_min(root << 1, l, r);
        else if (l > mid)
            return query_min(root << 1 | 1, l, r);
        else
            return min(query_min(root << 1, l, mid) , query_min(root << 1 | 1, mid + 1, r));
    }
};
Segment_Tree st;
void solve()
{
    int n,k;
    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    cin >> k;
    st.build(1, 1, n, a);
    for (int i = 1; i <= n; i++) {
        int l = max(1, i - k), r = min(n, k + i);
        if (i < n)
            cout << st.query_min(1, l, r) << ' ';
        else
            cout << st.query_min(1, l, r) << '\n';
    }
}
int  main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int T = 1;
    //    cin >> T;
    while (T--)
    {
        solve();
    }
    return 0;
}

分块

这题分块有可能可以过但也有可能过不了,取决于块大小的取值,试了不少的块取值还是没过最后一组样例

下面的代码块取得比较小,但是如果块大小取值改一下,数据再强一些,就不只是运行超时了,还会出现答案错误,可以自己去想一想然后试一下,这份代码运气好,所以只运行超时了

#include<iostream>
#include<vector>
#include<algorithm>
#include<set>
#include<cmath> 
using namespace std;
const int N = 1e6 + 10;
int a[N], b[N], mi[N], bg[N], ed[N], pos[N];
void slove() {
    int n, k;
    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    cin >> k;
    int block = sqrt(k), t;
    t = n / block;
    if (n % block)
        t++;
    for (int i = 1; i <= t; i++)
        bg[i] = block * (i - 1) + 1, ed[i] = block * i;
    ed[t] = n;
    for (int i = 1; i <= n; i++)
        pos[i] = (i - 1) / block + 1;
    for (int i = 1; i <= t; i++) {
        mi[i] = 0x3f3f3f3f;
        for (int j = bg[i]; j <= ed[i]; j++)
            mi[i] = min(a[j], mi[i]);
    }
    for (int i = 1; i <= n; i++) {
        int l = max(1, i - k), r = min(n, k + i), x, y;
        if (l > bg[pos[l]])
            x = pos[l] + 1;
        else
            x = pos[l];
        if (r < ed[pos[r]])
            y = pos[r] - 1;
        else
            y = pos[r];
        int ans = 0x3f3f3f3f;
        for (int i = l; i < bg[x]; i++)
            ans = min(a[i], ans);
        for (int i = x; i <= y; i++)
            ans = min(mi[i], ans);
        for (int i = bg[y] + 1; i <= r; i++)
            ans = min(a[i], ans);
        cout << ans;
        if (i < n)
            cout << ' ';
        else
            cout << '\n';
    }
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int T;
    T = 1;
    //cin >> T;
    while (T--) {
        slove();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值