zoukankan      html  css  js  c++  java
  • 【NOIP数据结构专项】单调队列单调栈

    【FZYZ P1280 】【NOIP福建夏令营】矩形覆盖

    Description

    有N个矩形,矩形的底边边长为1,且均在X轴上,高度给出,第i个矩形的高为h[i],求最少需要几个矩形才能覆盖这个图形。

    例如h = [3, 2, 4, 2]的图形如下:

    容易发现,只需要3个矩形就能覆盖这个图形。

    Input Format

    第一行一个整数N。接下来1行包含N个正整数,为h[i]。

    Output Format

    输出一个整数表示最少需要几个矩形能覆盖这个图形。

    Sample Input
    10
    2 3 2 4 2 1 3 4 3 2
    
    Sample Output
    7
    
    Hint

    对于所有数据,N<=100000,h[i] <= 100。

    对于部分数据,N<=10;

    对于部分数据,N<=100;

    对于部分数据,N<=1000;

    对于部分数据,h[i] <= 10;

    题解

    显然,若存在一个矩形的高度为x,则必然存在一个覆盖的矩形,其上边界高度为x.

    所以,若所有h[i]各不相同,就要用N个矩形才能覆盖这个图形.

    如果答案小于N,那么必然存在i,j(1<=i<j<=N),使得h[i]=h[j],且对任意k满足i<k<j,有h[k]>=h[i]且h[k]>=h[j](否则该覆盖矩形会超出图形的范围).

    单调栈维护严格上升序列即可

    #include<iostream>
    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    #include<stack>
    using namespace std;
    
    int n,ans;
    int h[100005];
    stack<int> q;
    
    int main()
    {
        int i,j;
        scanf("%d",&n);
        ans=n;
        for(i=1;i<=n;i++) scanf("%d",&h[i]);
        q.push(h[1]);
        for(i=2;i<=n;i++)
        {
            while(!q.empty()&&q.top()>=h[i])
            {
                if(q.top()==h[i]) ans--;
                q.pop();
            }
            q.push(h[i]);
        }
        printf("%d",ans);
        return 0;
    }

    【洛谷P1901 】发射站

    http://www.luogu.org/problem/show?pid=1901

    题目描述

    某地有 N 个能量发射站排成一行,每个发射站 i 都有不相同的高度 Hi,并能向两边(当 然两端的只能向一边)同时发射能量值为 Vi 的能量,并且发出的能量只被两边最近的且比 它高的发射站接收。

    显然,每个发射站发来的能量有可能被 0 或 1 或 2 个其他发射站所接受,特别是为了安 全,每个发射站接收到的能量总和是我们很关心的问题。由于数据很多,现只需要你帮忙计 算出接收最多能量的发射站接收的能量是多少。

    输入格式

    第 1 行:一个整数 N;

    第 2 到 N+1 行:第 i+1 行有两个整数 Hi 和 Vi,表示第 i 个人发射站的高度和发射的能量值。

    输出格式

    输出仅一行,表示接收最多能量的发射站接收到的能量值,答案不超过 longint。

    输入样例:

    3
    4 2 
    3 5 
    6 10
    

    输出样例:

    7

    数据规模

    对于 40%的数据,1<=N<=5000;1<=Hi<=100000;1<=Vi<=10000;

    对于 70%的数据,1<=N<=100000;1<=Hi<=2,000,000,000;1<=Vi<=10000;

    对于 100%的数据,1<=N<=1000000;1<=Hi<=2,000,000,000;1<=Vi<=10000。

    题解

    单调栈维护找出向左(右)边的第一个比自己大的数。

    #include<iostream>
    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    #include<stack>
    using namespace std;
    
    long long h[1000001],p[1000001],sum[1000001];
    struct hh{int l,r;};
    hh f[1000001];
    long long ans;
    int n,cnt;
    stack<int> s;
    
    int main()
    {
        int i,j;
        scanf("%d",&n);
        for(i=1;i<=n;i++)
          scanf("%lld%lld",&h[i],&p[i]);
        s.push(1);
        for(i=2;i<=n;++i)
        {
            while(!s.empty() && h[i]>h[s.top()]) f[s.top()].r=i,s.pop();
            s.push(i);
        }
        while(!s.empty()) s.pop();
        s.push(n);
        for(i=n-1;i>=1;--i)
        {
            while(!s.empty() && h[i]>h[s.top()]) f[s.top()].l=i,s.pop();
            s.push(i);
        }
    
        for(i=1;i<=n;i++){sum[f[i].l]+=p[i]; sum[f[i].r]+=p[i];}
        for(i=1;i<=n;i++) ans=max(ans,sum[i]);
        printf("%lld",ans);
        return 0;
        
    }

    【POJ2823 】【FZYZ P1561】滑动窗口

    http://poj.org/problem?id=2823

    Description

    给你一个长度为N的数组,一个长为K的滑动的窗体从最左移至最右端,你只能见到窗口的K个数,每次窗体向右移动一位,如下表:

    你的任务是找出窗口在各位置时的max value,min value.

    Input Format

    第1行n,k,第2行为长度为n的数组

    Output Format

    2行,第1行每个位置的min value,第2行每个位置的max value

    Sample Input
    8 3 
    1 3 -1 -3 5 3 6 7 
    Sample Output
    -1 -3 -3 -3 3 3 
    3 3 5 5 6 7 
    Hint

    20%:n<=500;

    50%:n<=100000;

    100%:n<=1000000;

    题解

    双端单调队列维护

    #include<iostream>
    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    using namespace std;
    int n,k;
    int a[1000005],maxx[1000005],minn[1000005];
    int q[1000005];
    
    int main()
    {
        int i,j,head=1,tail=0;
        scanf("%d%d",&n,&k);
        for(i=1;i<=n;i++) scanf("%d",&a[i]);
        
        head=1;tail=0;q[++tail]=1;
        for(i=2;i<=k;i++)
        {
            while(head<=tail&&a[q[tail]]<a[i]) tail--;
            q[++tail]=i;
        }
        maxx[1]=a[q[head]];
        for(i=k+1;i<=n;i++)
        {
            while(head<=tail&&a[q[tail]]<a[i]) tail--;
            while(head<=tail&&i-q[head]>=k) head++;
            q[++tail]=i;
            maxx[i-k+1]=a[q[head]];
        }
        
        head=1;tail=0;
        q[++tail]=1;
        for(i=2;i<=k;i++)
        {
            while(head<=tail&&a[q[tail]]>a[i]) tail--;
            q[++tail]=i;
        }
        minn[1]=a[q[head]];
        for(i=k+1;i<=n;i++)
        {
            while(head<=tail&&a[q[tail]]>a[i]) tail--;
            while(head<=tail&&i-q[head]>=k) head++;
            q[++tail]=i;
            minn[i-k+1]=a[q[head]];
        }
        for(i=1;i<=n-k+1;i++) printf("%d ",minn[i]);printf("
    ");
        for(i=1;i<=n-k+1;i++) printf("%d ",maxx[i]);
        return 0;
    }

    【ZLD NOIP模拟题】序列

    【问题描述】

    有一个长度为n的非负整数序列a_1,a_2,a_3$cdots$a_n,每次操作可以选择相邻的两个数a_i,a_{i+1},删去它们,然后在这个位置插入一个数max(a_i,a_{i+1}),此次操作的代价定义为max(a_i,a_{i+1}),求将这个序列长度变为1的最少代价。

    【输入格式】

    第一行为一个正整数n,表示序列的长度。

    第二行有n个非负整数a_1,a_2,a_3$cdots$a_n,表示这个序列。

    【输出格式】

    一行一个数,表示最少代价。

    【输入输出样例】

    seq.in

    seq.out

    3

    1 2 3

    5

    【数据规模】

    对于30%的数据,$nleq$10

    对于50%的数据,$nleq$5000

    对于100%的数据,1$leq$n$leq$10^6,0$leq$a_i$leq10^9$

    题解

    算法1:我们从左往右读入序列,用一个单调栈,当我们新加进去的数>栈顶时,那么便将栈顶的数拿去跟前一个或者当前的数合并(看哪个比较小),直到不满足这个条件,此时就把当前这个数塞入栈,最后注意一下栈中剩余的元素也是要合并的。

    #include<iostream>
    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    #include<stack>
    using namespace std;
    int n;
    long long ans;
    int a[1000005];
    stack<int> q;
    
    int main()
    {
        int i,j,temp,in;
        scanf("%d",&n);
        for(i=1;i<=n;i++) scanf("%d",&a[i]);
        q.push(1000000005);
        for(i=1;i<=n;i++)
        {
            if(!q.empty()&&q.top()>a[i])
            {
                q.push(a[i]);
                continue;
            }
            while(!q.empty()&&q.top()<a[i])
            {
                q.pop();
                if(a[i]>q.top()) ans+=(long long)q.top();
                else ans+=(long long)a[i];
            }
            q.push(a[i]);
        }
        if(!q.empty()&&q.size()!=2)
        {
            q.pop();
            while(!q.empty())
            {
                if(q.size()==1) goto hhh;
                ans+=(long long)q.top();
                q.pop();
            }
        }
        hhh:;
        printf("%lld",ans);
        return 0;
    }

    算法2:

    标程做法。我们考虑相邻的两个数x,y,不妨设x>y,则我们先在x右端找一条最长的以y为左端点的区间,使区间内所有的数<=x。

    然后呢我们将这些数合并起来,再与x合并,那么这个代价就是x了。

    这下子做法就出来了,答案就是sum_{i=1}^{n-1}max(a_i,a_{i+1})

    代码略。

  • 相关阅读:
    R语言:提取路径中的文件名字符串(basename函数)
    课程一(Neural Networks and Deep Learning),第三周(Shallow neural networks)—— 0、学习目标
    numpy.squeeze()的用法
    课程一(Neural Networks and Deep Learning),第二周(Basics of Neural Network programming)—— 4、Logistic Regression with a Neural Network mindset
    Python numpy 中 keepdims 的含义
    课程一(Neural Networks and Deep Learning),第二周(Basics of Neural Network programming)—— 3、Python Basics with numpy (optional)
    课程一(Neural Networks and Deep Learning),第二周(Basics of Neural Network programming)—— 2、编程作业常见问题与答案(Programming Assignment FAQ)
    课程一(Neural Networks and Deep Learning),第二周(Basics of Neural Network programming)—— 0、学习目标
    课程一(Neural Networks and Deep Learning),第一周(Introduction to Deep Learning)—— 0、学习目标
    windows系统numpy的下载与安装教程
  • 原文地址:https://www.cnblogs.com/yljiang/p/5738552.html
Copyright © 2011-2022 走看看