单调栈练习——Poj3250题 Bad Hair Day

Bad Hair Day

在做题之前记得复习
单调栈初步学习
单调队列初步学习

Description

Some of Farmer John’s N cows (1 ≤ N ≤ 80,000) are having a bad hair day! Since each cow is self-conscious about her messy hairstyle, FJ wants to count the number of other cows that can see the top of other cows’ heads.

Each cow i has a specified height hi (1 ≤ hi ≤ 1,000,000,000) and is standing in a line of cows all facing east (to the right in our diagrams). Therefore, cow i can see the tops of the heads of cows in front of her (namely cows i+1, i+2, and so on), for as long as these cows are strictly shorter than cow i.

Let ci denote the number of cows whose hairstyle is visible from cow i; please compute the sum of c1 through cN.For this example, the desired is answer 3 + 0 + 1 + 0 + 1 + 0 = 5.

Input

Line 1: The number of cows, N.
Lines 2…N+1: Line i+1 contains a single integer that is the height of cow i.

Output

Line 1: A single integer that is the sum of c1 through cN.

Sample Input

6
10 3 7 4 12 2

Sample Output

5

题意:

一群高度不完全相同的牛从左到右站成一排,每头牛只能看见它右边的比它矮的牛的发型,

若遇到一头高度大于或等于它的牛,则无法继续看到这头牛后面的其他牛,给出这些牛的高度,

要求每头牛可以看到的牛的数量的和。
一头牛的高度等于另一头牛的高度时,也认为看不到。

思路一:

求一头牛能看见几头牛

从后往前读入输入序列,统计每头牛能看到多少头牛,此时只考虑右边的牛,即栈内的牛。同时,必须再额外开一个数组ans进行记录每头牛看到的牛的数量 思考为什么。初始时最后一头牛入栈,最右边什么也没有,肯定什么也看不到,ans[n]记录为0,当下一头牛来时,判断其与栈顶元素的关系,若是小于等于,则看不到,若是大于,则可以看到,此时栈顶元素出栈,一直循环 (递减的单调栈),直到其小于等于栈顶元素或者为空(为空说明它右边的牛它都能看到),同时要注意,在出栈的同时,必须要记录一下每头牛身后能看到的牛的数量。
设想一种极端情况:
如果有一头牛身高特别高,右边的牛都能看到,那么栈内的元素将清空,最后只剩下这头牛,同时这头牛又有一个数组记录它能往右边看到的牛的数量x,这样,当下一头牛来时,如果它又比上一头牛高,此时虽然只出栈了一次,但它看到的牛的数量却为x+1。

#include <bits/stdc++.h>

using namespace std;

const int N=1e5;
stack<int> stk;
int height[N],ans[N];

int main() {
    
    
    int n;
    long long res=0;
    scanf("%d",&n);
    for (int i = 0; i < n; ++i)  scanf("%d",height+i);
    stk.push(n-1); //最后一个右边必没有,ans[n-1]=0
    for (int i = n-2; i >= 0; --i) {
    
    
        while (!stk.empty() && height[i] > height[stk.top()]){
    
      //此题中等于的话也看不到。
            ans[i]++;   ans[i]+=ans[stk.top()];
            stk.pop();
        }
        stk.push(i);
    }
    for (int i = 0; i < n; ++i)   res+=ans[i];
    printf("%lld ",res);
    return 0;
}

思路二

换个角度想,统计每头牛可以被几头牛看到,怎么样呢?
思路一中统计每头牛看到几头牛,需要看右边的牛,从后往前读入数组;思路二我们只需考虑左边的牛即可,因为牛都是向右看的,新入栈的牛只能被左边的牛看到。
一头牛c,只能被左边高于它的牛看到,如果有这种情况,高于牛c的牛中间,有一只牛a身高低,且牛a右边的牛b身高高于牛a,此时牛a就 了,不需要再统计了,因为它已经什么也看不到了。这类似于寻找左边第一个小于它的数一样的性质。
因此,牛c入栈时,如果栈顶元素的牛的身高 小于等于 牛c,则将栈顶元素出栈,因为看不到牛c且以后就再也没用了,会被牛c挡到什么也看不到,直到碰到第一个大于牛c的栈顶元素或者栈空(牛c太高了)时,循环停止。此时栈内元素个数就是它能被看到的个数。
最后构成了一个递减的单调栈!

#include <bits/stdc++.h>

using namespace std;

const int N=1e5;
int stk[N],tt;//模拟数组,tt指tt.size(),栈内元素数量,tt为 0 表示栈空,

int main()
{
    
    
    int n,x;
    long long res=0;
    scanf("%d",&n);
    while (n--){
    
    
        scanf("%d",&x);
        while (tt && x >= stk[tt]) tt--;//出栈
        res+=tt; //统计栈内元素个数
        stk[++tt]=x;//入栈
    }
    printf("%lld",res);
}

为什么上面用long long定义res呢?

大概估算一下结果的最大值:牛的头数Nmax=8e4,当初始时就是单调递减的输入时,此时不需优化,可全部进栈。res = N*(N+1)/2 ≈ 3e9,而int的最大范围大概为2e9。 不开long long见祖宗。

知识拓展

C++的STL中accumulate的用法:

auto sum = accumulate(vec.begin() , vec.end() , 42);

accumulate带有三个形参:头两个形参指定要累加的元素范围,第三个形参则是累加的初值。
accumulate函数将它的一个内部变量设置为指定的初始值,然后在此初值上累加输入范围内所有元素的值。
accumulate返回累加的结果,其返回类型就是其第三个实参的类型。

可以使用accumulate把string型的vector容器中的元素连接起来:

string sum = accumulate(v.begin() , v.end() , string(" "));

这个函数调用的效果是:从空字符串开始,把vec里的每个元素连接成一个字符串。

#include <bits/stdc++.h>

using namespace std;

int main()
{
    
    
    int n,a[1010],res1=0;
    cin>>n;
    for (int i = 0; i < n; ++i) cin>>a[i];
    for (int i = 0; i < n; ++i) {
    
    
           res1+=a[i];
    }
    int t=0;
    auto res2=accumulate(a,a+n,t);
    cout<<res1<<endl<<res2<<endl;
}

猜你喜欢

转载自blog.csdn.net/HangHug_L/article/details/113556457
今日推荐