week5作业:单调栈、差分与前缀和、尺取法

A

题意

给定一个直方图,求直方图中可连成的最大矩形面积

思路

问题核心:单调递增栈
利用单调递增栈维护各个下标(左端点下标,利用各个高度判断大小关系)
面积=下标之差*高度

总结

注意数据范围,本题需使用long long

代码

#include<cstdio>
#include<stack>
using namespace std;

const int MAXN=1e6;
long long n,h[MAXN];

int main(void){
    while(1){
        scanf("%lld",&n);
        if(n==0)
            break;
        for(int i=0;i<n;i++)
            scanf("%lld",&h[i]);
        h[n]=-1;

        stack<int> stIndex;
        long long nowIndex,ans=0;
        for(int i=0;i<=n;i++){
            if(stIndex.empty()||h[stIndex.top()]<=h[i])
                stIndex.push(i);
            else{
                while(!stIndex.empty()&&h[stIndex.top()]>h[i]){
                    nowIndex=stIndex.top();
                    stIndex.pop();
                    long long area=(i-nowIndex)*h[nowIndex];
                    if(area>ans)
                        ans=area;
                }
                stIndex.push(nowIndex);
                h[nowIndex]=h[i];
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}

B

题意

每个城市都有一个价值,给定一个区间,为区间范围内的所有城市增加q次价值c

思路

题目定位:差分与前缀和
先用差分处理,在左端点的差分数组增加价值c,在右端点的差分数组减少价值c
用前缀和重新合成新的价值

总结

注意数据范围,需要用long long

代码

#include<cstdio>
using namespace std;

const int MAXN=1e7;
long long n,q,l,r,c;
long long a[MAXN];
long long B[MAXN];


int main(void){
    scanf("%lld%lld",&n,&q);
    for(int i=0;i<n;i++)
        scanf("%lld",&a[i]);
    B[0]=a[0];
    for(int i=1;i<n;i++)
        B[i]=a[i]-a[i-1];

    while(q--){
        scanf("%lld%lld%lld",&l,&r,&c);
        B[l-1]+=c;
        B[r]-=c;
    }
    for(int i=0;i<n;i++){
        if(i==0)
            a[0]=B[0];
        else
            a[i]=B[i]+a[i-1];
        printf("%lld ",a[i]);
    }
    printf("\n");

    return 0;
}

C

题意

字符串仅包含 ‘Q’, ‘W’, ‘E’, ‘R’ 四种字符。
如果四种字符在字符串中出现次数均为 n/4,则其为一个平衡字符串。将 s 中连续的一段子串替换成相同长度的只包含那四个字符的任意字符串,使其变为一个平衡字符串,问替换子串的最小长度?
如果 s 已经平衡则输出0。

思路

问题定位:尺取法(有连续的区间和明确的方向)

  1. 求出各字母次数,及最大的一个字母的次数
  2. 在l,r范围内判定,假设范围之外的字母不变,求出在该范围内替换掉所有需要字母的个数后,仍然剩下的可随意安排的空位置若大于等于0且为4的倍数则表示该范围可以满足,需要再缩小范围判断(l++)
  3. 否则需要扩大范围(r++)

总结

注意下标的计算和移动范围问题

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int MAXN=1e6;
const int INF=1e6;
int n;
char s[MAXN];
struct Sum{
    int sumQ;
    int sumW;
    int sumE;
    int sumR;
    Sum(){
        sumQ=0;
        sumW=0;
        sumE=0;
        sumR=0;
    }
    Sum(int q,int w,int e,int r)
        :sumQ(q),sumW(w),sumE(e),sumR(r) {}
    Sum operator -(const Sum&t){
        Sum temp(sumQ-t.sumQ,sumW-t.sumW,sumE-t.sumE,sumR-t.sumR);
        return temp;
    }
} sum[MAXN];


int main(void){
    scanf("%s",s);
    n=strlen(s);
    for(int i=0;i<n;i++){
        if(i!=0) sum[i]=sum[i-1];
        
        if(s[i]=='Q') sum[i].sumQ+=1;
        else if(s[i]=='W') sum[i].sumW+=1;
        else if(s[i]=='E') sum[i].sumE+=1;
        else if(s[i]=='R') sum[i].sumR+=1;
    }

    int l=0,r=0,ans=INF;
    while(l<n&&r<n){
        int TOTAL,FREE;
        Sum temp;
        if(l!=0) temp=sum[l-1];
        Sum t=sum[n-1]-(sum[r]-temp);
        int MAX=max(t.sumQ,max(t.sumW,max(t.sumE,t.sumR)));


        TOTAL=r-l+1;
        FREE=TOTAL-((MAX-t.sumQ)+(MAX-t.sumW)+(MAX-t.sumE)+(MAX-t.sumR));
        if(FREE>=0&&FREE%4==0){
            l++;
            if(TOTAL<ans)
                ans=TOTAL;
        }
        else 
            r++;
    }

    printf("%d\n",ans);


    return 0;
}

D

题意

一组数,一个窗口,不断向右移动,求出每次窗口所在位置是其中元素的最大、最小值

Window position Minimum value Maximum value
[1 3 -1] -3 5 3 6 7 -1 3
1 [3 -1 -3] 5 3 6 7 -3 3
1 3 [-1 -3 5] 3 6 7 -3 5
1 3 -1 [-3 5 3] 6 7 -3 5
1 3 -1 -3 [5 3 6] 7 3 6
1 3 -1 -3 5 [3 6 7] 3 7
输入:

8 3
1 3 -1 -3 5 3 6 7

输出:

-1 -3 -3 -3 3 3
3 3 5 5 6 7

思路

问题定位:单调队列
维护2个单调队列:递增(求最小值)、递减(求最大值)

以单调递增队列为例:

  1. 保证队列中只有窗口大小个元素:队列不为空,且元素下标<i-k+1;否则弹出队首元素。
  2. 从队尾插入下标,要求值递增,若队尾元素不满足大小关系,则从队尾弹出元素,直至能插入。
  3. 每次的队首元素即为最小元素

总结

单调队列用deque,涉及队首的删除、队尾的插入与删除

TLE说明:

  • 以窗口起点开始,时间是O(n^2),所以TLE
  • 改为从窗口末尾开始,利用 下标<i-k+1,时间是O(n)

代码

#include<cstdio>
#include<deque>
using namespace std;

const int MAXN=1e7;
int k,n,a[MAXN];

int main(void){
    scanf("%d%d",&n,&k);
    for(int i=0;i<n;i++)
        scanf("%d",&a[i]);
    deque<int> dqMinIndex,dqMaxIndex;
    for(int i=0;i<n;i++){
        while(!dqMinIndex.empty()&&dqMinIndex.front()<i-k+1)
            dqMinIndex.pop_front();
        while(!dqMinIndex.empty()&&a[dqMinIndex.back()]>=a[i])
            dqMinIndex.pop_back();
        dqMinIndex.push_back(i);
        if(i>=k-1)
            printf("%d ",a[dqMinIndex.front()]);
    }
    printf("\n");
    for(int i=0;i<n;i++){
        while(!dqMaxIndex.empty()&&dqMaxIndex.front()<i-k+1)
            dqMaxIndex.pop_front();
        while(!dqMaxIndex.empty()&&a[dqMaxIndex.back()]<=a[i])
            dqMaxIndex.pop_back();
        dqMaxIndex.push_back(i);
        if(i>=k-1)
            printf("%d ",a[dqMaxIndex.front()]);
    }
    printf("\n");

    return 0;
}
下附TLE代码
#include<cstdio>
#include<deque>
using namespace std;

const int MAXN=1e7;
int k,n,a[MAXN];

int main(void){
    scanf("%d%d",&n,&k);
    for(int i=0;i<n;i++)
        scanf("%d",&a[i]);
    deque<int> dqMinIndex,dqMaxIndex;
    for(int i=0;i<=n-k;i++){
        for(int j=i;j<i+k;j++){
            while(!dqMinIndex.empty()&&dqMinIndex.front()<i)
                dqMinIndex.pop_front();
            while(!dqMinIndex.empty()&&a[dqMinIndex.back()]>=a[j])
                dqMinIndex.pop_back();
            dqMinIndex.push_back(j);
        }
        printf("%d ",a[dqMinIndex.front()]);
    }
    printf("\n");
    for(int i=0;i<=n-k;i++){
        for(int j=i;j<i+k;j++){
            while(!dqMaxIndex.empty()&&dqMaxIndex.front()<i)
                dqMaxIndex.pop_front();
            while(!dqMaxIndex.empty()&&a[dqMaxIndex.back()]<=a[j])
                dqMaxIndex.pop_back();
            dqMaxIndex.push_back(j);
        }
        printf("%d ",a[dqMaxIndex.front()]);
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值