zoukankan      html  css  js  c++  java
  • 洛谷P1823:音乐会的等待(单调栈)

    P1823 [COI2007] Patrik 音乐会的等待

    题解:维护一个单调不递增的栈

    首先说明一下:由于一个人左边可以看到一些人,右边可以看到一些人,可以我们仅从一边考虑,对于一个人,我们考虑它的左边能看到多少个。两边都考虑会导致重复计算。

    tot是当前栈元素的个数

    对于新加入单调栈的一个元素,我们分三种情况讨论:

    1、新元素比栈顶小:直接加入栈,ans加1

    2、新元素与栈顶元素相等:ans加栈中与新元素相等的数的个数,同时若tot不为0,ans还应再加1

    3、新元素比栈顶大:不断弹出元素,每弹一个ans加一,一直弹到新元素<=栈顶元素,再同情况2进行判断即可。

    我们想想这个为什么是对的,两个人可以相互看见只有它们间没有人比他们高,对于加入栈的新元素,由于栈是不递增的。如果该元素比栈顶小,我们是从左边考虑,那么该元素仅能看到它的左边的第一个元素,也即是栈顶,栈顶左边的人都比栈顶高,这个新元素看不见。

    如果新元素和栈顶相等,那么我们就去寻找栈里面有多少和该元素相等的数,找到一个答案就加1,因为相等的元素能够相互看见,对答案的贡献就是1。如果找到最后,tot不为0,那么答案再加1,因为它俩也可以相互看见 比如说

    栈内有5 2 2,再加入一个2,答案增加3,因为新加入的2 和 5也可以相互看见

    如果新元素比栈顶大,那么就不断弹出元素,直到新元素<=栈顶元素,弹出一个,答案加1,因为弹出的元素比新元素小,因此新元素可以看见它的左边比它小的元素,有些人可能疑惑元素弹出以后对后面的计数有没有什么影响呢,答案是没有。

    新元素的身高比弹出的元素的身高大,也就是这些弹出的元素与新元素后面的元素之间不满足能相互看见,新元素的后面任何一个元素都不会看见这些弹出的元素。 到新元素<=栈顶元素时,再考虑新元素是否等于栈顶,这时就回到了情况2

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=5e5+5;
    int s[maxn],tot,n,tmp,ans;
    int main()
    {
        cin>>n;
        for(int i=1;i<=n;i++)
        {
            cin>>tmp;
            if(tmp<s[tot]) ans++,s[++tot]=tmp;
            else //这里情况2和3可以放在一起处理
            {
                int t=0;
                while(tot&&tmp>s[tot]) ans++,tot--;
                while(tot&&tmp==s[tot]) ans++,tot--,t++;
                if(tot) ans++;
                tot+=t;//恢复栈中相同的元素
                s[++tot]=tmp;
            }
        }
        cout<<ans;
        return 0;
    }
    View Code

    这个代码交上去以后最后三个测试点超时了,可以考虑优化,对于元素相同的情况,我们重复的计算栈内相同元素的个数,所以我们可以用一个数组来记录这个相同元素的个数。

    // luogu-judger-enable-o2
    // luogu-judger-enable-o2
    // luogu-judger-enable-o2
    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=5e5+5;
    int s[maxn],w[maxn],tot,n,tmp;
    long long ans;
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&tmp);
            if(tmp<s[tot]) ans+=1LL,s[++tot]=tmp,w[tot]=1;
            else 
            {
                int width=0;
                while(tot&&tmp>s[tot]) ans+=1LL*w[tot],tot--;
                while(tot&&tmp==s[tot]) ans+=1LL*w[tot],width+=w[tot],tot--;
                if(tot) ans+=1LL;
                s[++tot]=tmp;
                w[tot]=width+1;
            }
        }
        printf("%lld",ans);
        return 0;
    }
    View Code

    还有就是答案一定要用long long,不然最后三个会WA

  • 相关阅读:
    Core Data入门
    web前端开发与iOS终端开发的异同
    Blocks编程
    ARC下循环引用的问题
    小项目
    error: /Users/**/Documents/workspace/***/clean_right_normal.png: No such file or directory
    修复NSTextAlignmentCenter引起的警告
    Implicit conversion loses integer precision: 'long long' to 'NSUInteger' (aka 'unsigned int')
    手势相关函数
    implicit declaration of function setxattr is invalid in c99
  • 原文地址:https://www.cnblogs.com/xiaoguapi/p/10364846.html
Copyright © 2011-2022 走看看