zoukankan      html  css  js  c++  java
  • 模拟单调栈

    例题1:链接:http://acm.hnucm.edu.cn/JudgeOnline/problem.php?id=1329

    有n个数,每个数有权值。数学老师定义了区间价值为区间和乘上区间内的最小值。

    现在要你找出有最大区间价值的区间是什么,并输出区间价值
    输入:
    每个输入文件只包含单组数据。
    第一行一个整数n。(1 <= n <= 100000)
    第二行n个整数a_1,a_2,...,a_n。(0 <= a_i <= 1000000)
    输出:
    第一行输出一个整数,表示最大的区间价值。
    第二行输出两个整数,表示区间的起点和终点。
    保证答案唯一。
    Sample Input:
    6
    10 1 9 4 5 9
    Sample Output:
    108
    3  6
    此题求区间和,以及区间的最小值,我们可以使用单调栈的思想
    设定L[]数组,记录以a[i]为最小值的左边边界;
    设定R[]数组,记录以a[i]为最小值的右边边界;
    这样,样例中的L,R数组为
    位置:1 2 3 4 5 6
             10 1 9 4 5 9
    L        1  2 3 3 5 6
    R       1  6 3 6 6 6
    这样,我们就可以通过计算 R[i]-L[i] 来得出区间的范围,代码怎么实现呢?

    以上代码为模拟单调栈的核心代码

    由于区间价值是区间和*区间的最小值,所以我们要算出区间和,在这里我们使用前缀和

    读取数据时计算:

     算出R[i]、L[i]后;

    接下来的比较我就不说明了~

    AC代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn=1e6;
    ll sum[maxn];
    int L[maxn],R[maxn];
    int a[maxn];
    int main() {
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            sum[i]+=sum[i-1]+a[i];
        }
        for(int i=1;i<=n;i++)
        {
            int l=i;
            while(a[i]<=a[l-1]&&l>1)  //判断左边以a[i]为最小值能到哪
            {
                l=L[l-1];  //如果比a[l-1]要小,那么它能到达以a[l-1]为最小值的左边
            }
            L[i]=l;
        }
        for(int i=n;i>=1;i--)
        {
            int r=i;
            while(a[i]<=a[r+1]&&r<n)  //判断右边以a[i]为最小值能到哪
            {
                r=R[r+1];//如果比a[r+1]要小,那么它能到达以a[r+1]为最小值的右边
            }
            R[i]=r;
        }
        ll ans = -1,l,r;
        for(int i=1;i<=n;i++)
        {
            ll res=(sum[R[i]]-sum[L[i]-1])*a[i];
            if(res>ans)
            {
                ans=res;
                l=L[i];
                r=R[i];
            }
        }
        printf("%lld
    ",ans);
        printf("%lld %lld
    ",l,r);
        return 0;
    }
    View Code

      例题2:链接:https://ac.nowcoder.com/acm/problem/15815

    这道题可以学完上面之后加以强化

    注意:

    1、计算求和区间的最大值与最小值的差=求出所有区间的最大值-所有区间的最小值==(以a[i]为最大值的区间*区间数)-(以a[i]为最小值的区间*区间数)

    2、类似1 3 1出现重复值序列,使用一次 = 判断即可,不然会减去多一个重复值

    3、数组不要太大,容易内存超限!

    4、int *int 会出现溢出,因此计算时最好化为 ll ~

    AC代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn=1e6+5;
    int L[maxn],R[maxn];
    int a[maxn];
    int main() {
        int n;
        while(~scanf("%d",&n))
       {
           memset(L,0,sizeof(L));
           memset(R,0,sizeof(R));
           for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }
        for(int i=1;i<=n;i++)
        {
            int l=i;
            while(a[i]<a[l-1]&&l>1)  //判断左边以a[i]为最小值能到哪
            {
                l=L[l-1];  //如果比a[l-1]要小,那么它能到达以a[l-1]为最小值的左边
            }
            L[i]=l;
        }
        for(int i=n;i>=1;i--)
        {
            int r=i;
            while(a[i]<=a[r+1]&&r<n)  //判断右边以a[i]为最小值能到哪
            {
                r=R[r+1];//如果比a[r+1]要小,那么它能到达以a[r+1]为最小值的右边
            }
            R[i]=r;
        }
        ll sum1=0;
        for(int i=1;i<=n;i++)
        {
           sum1-=(ll(i-L[i]+1)*(R[i]-i+1))*a[i];
        }
    //*****************************************
        for(int i=1;i<=n;i++)
        {
            int l=i;
            while(a[i]>a[l-1]&&l>1)  //判断左边以a[i]为最大值能到哪
            {
                l=L[l-1];  //如果比a[l-1]要大,那么它能到达以a[l-1]为最小值的左边
            }
            L[i]=l;
        }
        for(int i=n;i>=1;i--)
        {
            int r=i;
            while(a[i]>=a[r+1]&&r<n)  //判断右边以a[i]为最大值能到哪
            {
                r=R[r+1];//如果比a[r+1]要大,那么它能到达以a[r+1]为最小值的右边
            }
            R[i]=r;
        }
        for(int i=n;i>=1;i--)
        {
            sum1+=(ll(i-L[i]+1)*(R[i]-i+1))*a[i];
        }
        printf("%lld
    ",sum1);
       }
        return 0;
    }
    View Code

    祝大家都能AC~

     
  • 相关阅读:
    hdu 6182A Math Problem(快速幂)
    861. 二分图的最大匹配(匈牙利算法模板)
    860. 染色法判定二分图(模板)
    859. Kruskal算法求最小生成树(模板)
    858. Prim算法求最小生成树(模板)
    洛谷 P2577 [ZJOI2005]午餐
    洛谷 P2286 [HNOI2004]宠物收养场
    【模板】Splay
    P2234 [HNOI2002]营业额统计
    洛谷 P3369 【模板】普通平衡树
  • 原文地址:https://www.cnblogs.com/acmer-hmin/p/11748087.html
Copyright © 2011-2022 走看看