备战蓝桥杯---数据结构与STL应用(入门3)

文章讲述了如何使用优先队列(如deque和priority_queue)以及贪心策略来解决与高度相关的问题,涉及对顶堆的图解和针对不同场景的代码实现。主要讨论了如何在特定条件下找到最优解并维护元素顺序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们先来一道题作为过渡:

我们只需枚举n,选出左右第一个小于它高度的坐标即可,于是我们可以用两个方向的优先队列来维护,下面是AC代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n;
struct node{
    int index,tall;
}a[100010];
int b[100010],b1[100010];
signed main(){
    cin>>n;
    while(n!=0){
        for(int i=1;i<=n;i++){
            cin>>a[i].tall;
            a[i].index=i;
        }
        deque<node> q1;
        deque<node> q2;
        for(int i=1;i<=n;i++){
            while(!q1.empty()&&a[i].tall<q1.back().tall){
                q1.pop_back();}
            int f=0;
            if(!q1.empty()) {if(a[i].tall==q1.back().tall){
                f=1;
            }}
            if(q1.empty()) b[i]=0;
            else{
                if(f==0) b[i]=q1.back().index; 
                else b[i]=b[q1.back().index];
            }   
            q1.push_back(a[i]);
        }
        for(int i=n;i>=1;i--){
            while(!q2.empty()&&a[i].tall<q2.front().tall){
                q2.pop_front();}
            int f=0;
            if(!q2.empty()){if(a[i].tall==q2.back().tall){
                f=1;
            }}
            if(q2.empty()) b1[i]=n+1;
            else{
                if(f==0) b1[i]=q2.front().index;
                else b1[i]=b1[q2.back().index];
            }
            q2.push_front(a[i]);
        }
        long long max1=0;
        for(int i=1;i<=n;i++){
            max1=max(max1,a[i].tall*(b1[i]-b[i]-1));
        }
        cout<<max1<<endl;
        cin>>n;
    }
}

这里注意一下:

当两个高度相等时,我们要特殊处理一下,一方面,我们把它们正常添加以保证后面元素不出错。另一方面,他们的值应该定位到第一个出现该高度的值。

接下来,我们主要围绕优先队列来讲:

下面是分析:

其实这是一个典型的对顶堆,下面是图解:

下面是AC代码:

#include<bits/stdc++.h>
using namespace std;
int p,m,xu,jj;
int main(){
    cin>>p;
    while(p--){
    priority_queue<int> q1;
    priority_queue<int,vector<int>,greater<int> > q2;
    cin>>xu>>m;
    cout<<xu<<" "<<(m+1)/2<<endl;
    int kk=0;
    for(int i=1;i<=m;i++){
        scanf("%d",&jj);
       if(q1.empty()&&q2.empty()) q1.push(jj);
       else if(q1.empty()||q2.empty()){
           if(q1.empty()){
               if(jj>=q2.top()) q2.push(jj);
               else q1.push(jj);
           }
           else{
               if(jj<=q1.top()) q1.push(jj);
               else q2.push(jj);
           }
       }
        else{
             if(jj>=q2.top()) q2.push(jj);
               else q1.push(jj);
        }
        if(q1.size()>q2.size()+1){
            q2.push(q1.top());
            q1.pop();
        }
        if(q2.size()>q1.size()+1){
            q1.push(q2.top());
            q2.pop();
        }
        if(i%2==1){
            if(kk==10) {cout<<endl;
                        kk=0;}
            if(q1.size()>q2.size()) printf("%d ",q1.top());
            else printf("%d ",q2.top());
            kk++;
        }
    }
    if(p!=0) cout<<endl;
    }
}

接下来来个十分好的问题:

下面为分析:

首先,我们容易想到用贪心,那怎么贪心呢?

其实,我们把每个元素在剩余序列上第一个位置最远的元素替换即可,这样子,我们保证它们会相比其他决策最先遇到与自己一样的值,也保证剩余序列上与缓存上的一样的值相比其他决策最靠前,因此正确性显然。

那我们如何维护呢?先用map把数据离散化,在维护一个next数组,序列上第一个位置最远的元素用优先队列维护即可。

注意,当i遇到重复的j直接加进即可,在j没离开时,i肯定不会在栈顶。j离开后,因为next[i]=j,后加的元素肯定》j,于是i就被一直忽略。

下面是AC代码:

#include<bits/stdc++.h>
using namespace std;
int n,m,a[100010],tot,next1[100010],cun[100010],cnt,sum;
map<int,int> mp;
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=n;i>=1;i--){
        if(mp.count(a[i])==0) next1[i]=n+1;
        else next1[i]=mp[a[i]];
        mp[a[i]]=i;
    }
    priority_queue<int> q;
    for(int i=1;i<=n;i++){
        if(cun[i]==1){
            q.push(next1[i]);
            cun[next1[i]]=1;
        }
        else{
            if(cnt<m){
                q.push(next1[i]);
                 cun[next1[i]]=1;
                sum++;
                cnt++;
            }
            else{
                cun[q.top()]=0;
                q.pop();
                q.push(next1[i]);
                 cun[next1[i]]=1;
                sum++;
            }
        }
    }
    cout<<sum;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值